]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/analyzer/sm-malloc.cc
analyzer: decls are not on the heap
[thirdparty/gcc.git] / gcc / analyzer / sm-malloc.cc
1 /* A state machine for detecting misuses of the malloc/free API.
2 Copyright (C) 2019-2020 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
4
5 This file is part of GCC.
6
7 GCC is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
10 any later version.
11
12 GCC is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
20
21 #include "config.h"
22 #include "system.h"
23 #include "coretypes.h"
24 #include "tree.h"
25 #include "function.h"
26 #include "basic-block.h"
27 #include "gimple.h"
28 #include "options.h"
29 #include "bitmap.h"
30 #include "diagnostic-path.h"
31 #include "diagnostic-metadata.h"
32 #include "function.h"
33 #include "analyzer/analyzer.h"
34 #include "diagnostic-event-id.h"
35 #include "analyzer/analyzer-logging.h"
36 #include "analyzer/sm.h"
37 #include "analyzer/pending-diagnostic.h"
38 #include "tristate.h"
39 #include "selftest.h"
40 #include "analyzer/call-string.h"
41 #include "analyzer/program-point.h"
42 #include "analyzer/store.h"
43 #include "analyzer/region-model.h"
44
45 #if ENABLE_ANALYZER
46
47 namespace ana {
48
49 namespace {
50
51 class api;
52 class malloc_state_machine;
53
54 /* An enum for discriminating between different kinds of allocation_state. */
55
56 enum resource_state
57 {
58 /* States that are independent of api. */
59
60 /* The start state. */
61 RS_START,
62
63 /* State for a pointer that's known to be NULL. */
64 RS_NULL,
65
66 /* State for a pointer that's known to not be on the heap (e.g. to a local
67 or global). */
68 RS_NON_HEAP,
69
70 /* Stop state, for pointers we don't want to track any more. */
71 RS_STOP,
72
73 /* States that relate to a specific api. */
74
75 /* State for a pointer returned from the api's allocator that hasn't
76 been checked for NULL.
77 It could be a pointer to heap-allocated memory, or could be NULL. */
78 RS_UNCHECKED,
79
80 /* State for a pointer returned from the api's allocator,
81 known to be non-NULL. */
82 RS_NONNULL,
83
84 /* State for a pointer passed to the api's deallocator. */
85 RS_FREED
86 };
87
88 /* Custom state subclass, which can optionally refer to an an api. */
89
90 struct allocation_state : public state_machine::state
91 {
92 allocation_state (const char *name, unsigned id,
93 enum resource_state rs, const api *a)
94 : state (name, id), m_rs (rs), m_api (a)
95 {}
96
97 void dump_to_pp (pretty_printer *pp) const FINAL OVERRIDE;
98
99 const allocation_state *get_nonnull () const;
100
101 enum resource_state m_rs;
102 const api *m_api;
103 };
104
105 /* An enum for choosing which wording to use in various diagnostics
106 when describing deallocations. */
107
108 enum wording
109 {
110 WORDING_FREED,
111 WORDING_DELETED
112 };
113
114 /* Represents a particular family of API calls for allocating/deallocating
115 heap memory that should be matched e.g.
116 - malloc/free
117 - scalar new/delete
118 - vector new[]/delete[]
119 etc.
120
121 We track the expected deallocation function, but not the allocation
122 function - there could be more than one allocator per deallocator. For
123 example, there could be dozens of allocators for "free" beyond just
124 malloc e.g. calloc, xstrdup, etc. We don't want to explode the number
125 of states by tracking individual allocators in the exploded graph;
126 we merely want to track "this value expects to have 'free' called on it".
127 Perhaps we can reconstruct which allocator was used later, when emitting
128 the path, if it's necessary for precision of wording of diagnostics. */
129
130 struct api
131 {
132 api (malloc_state_machine *sm,
133 const char *name,
134 const char *dealloc_funcname,
135 enum wording wording);
136
137 /* An internal name for identifying this API in dumps. */
138 const char *m_name;
139
140 /* The name of the deallocation function, for use in diagnostics. */
141 const char *m_dealloc_funcname;
142
143 /* Which wording to use in diagnostics. */
144 enum wording m_wording;
145
146 /* Pointers to api-specific states.
147 These states are owned by the state_machine base class. */
148
149 /* State for an unchecked result from this api's allocator. */
150 state_machine::state_t m_unchecked;
151
152 /* State for a known non-NULL result from this apis's allocator. */
153 state_machine::state_t m_nonnull;
154
155 /* State for a value passed to this api's deallocator. */
156 state_machine::state_t m_freed;
157 };
158
159 /* A state machine for detecting misuses of the malloc/free API.
160
161 See sm-malloc.dot for an overview (keep this in-sync with that file). */
162
163 class malloc_state_machine : public state_machine
164 {
165 public:
166 typedef allocation_state custom_data_t;
167
168 malloc_state_machine (logger *logger);
169
170 state_t
171 add_state (const char *name, enum resource_state rs, const api *a);
172
173 bool inherited_state_p () const FINAL OVERRIDE { return false; }
174
175 state_machine::state_t
176 get_default_state (const svalue *sval) const FINAL OVERRIDE
177 {
178 if (tree cst = sval->maybe_get_constant ())
179 {
180 if (zerop (cst))
181 return m_null;
182 }
183 if (const region_svalue *ptr = sval->dyn_cast_region_svalue ())
184 {
185 const region *reg = ptr->get_pointee ();
186 const region *base_reg = reg->get_base_region ();
187 if (base_reg->get_kind () == RK_DECL
188 || base_reg->get_kind () == RK_STRING)
189 return m_non_heap;
190 }
191 return m_start;
192 }
193
194 bool on_stmt (sm_context *sm_ctxt,
195 const supernode *node,
196 const gimple *stmt) const FINAL OVERRIDE;
197
198 void on_phi (sm_context *sm_ctxt,
199 const supernode *node,
200 const gphi *phi,
201 tree rhs) const FINAL OVERRIDE;
202
203 void on_condition (sm_context *sm_ctxt,
204 const supernode *node,
205 const gimple *stmt,
206 tree lhs,
207 enum tree_code op,
208 tree rhs) const FINAL OVERRIDE;
209
210 bool can_purge_p (state_t s) const FINAL OVERRIDE;
211 pending_diagnostic *on_leak (tree var) const FINAL OVERRIDE;
212
213 bool reset_when_passed_to_unknown_fn_p (state_t s,
214 bool is_mutable) const FINAL OVERRIDE;
215
216 api m_malloc;
217 api m_scalar_new;
218 api m_vector_new;
219
220 /* States that are independent of api. */
221
222 /* State for a pointer that's known to be NULL. */
223 state_t m_null;
224
225 /* State for a pointer that's known to not be on the heap (e.g. to a local
226 or global). */
227 state_t m_non_heap; // TODO: or should this be a different state machine?
228 // or do we need child values etc?
229
230 /* Stop state, for pointers we don't want to track any more. */
231 state_t m_stop;
232
233 private:
234 void on_allocator_call (sm_context *sm_ctxt,
235 const gcall *call,
236 const api &ap) const;
237 void on_deallocator_call (sm_context *sm_ctxt,
238 const supernode *node,
239 const gcall *call,
240 const api &ap) const;
241 void on_zero_assignment (sm_context *sm_ctxt,
242 const gimple *stmt,
243 tree lhs) const;
244 };
245
246 /* struct api. */
247
248 api::api (malloc_state_machine *sm,
249 const char *name,
250 const char *dealloc_funcname,
251 enum wording wording)
252 : m_name (name),
253 m_dealloc_funcname (dealloc_funcname),
254 m_wording (wording),
255 m_unchecked (sm->add_state ("unchecked", RS_UNCHECKED, this)),
256 m_nonnull (sm->add_state ("nonnull", RS_NONNULL, this)),
257 m_freed (sm->add_state ("freed", RS_FREED, this))
258 {
259 }
260
261 /* Return STATE cast to the custom state subclass, or NULL for the start state.
262 Everything should be an allocation_state apart from the start state. */
263
264 static const allocation_state *
265 dyn_cast_allocation_state (state_machine::state_t state)
266 {
267 if (state->get_id () == 0)
268 return NULL;
269 return static_cast <const allocation_state *> (state);
270 }
271
272 /* Return STATE cast to the custom state subclass, for a state that is
273 already known to not be the start state . */
274
275 static const allocation_state *
276 as_a_allocation_state (state_machine::state_t state)
277 {
278 gcc_assert (state->get_id () != 0);
279 return static_cast <const allocation_state *> (state);
280 }
281
282 /* Get the resource_state for STATE. */
283
284 static enum resource_state
285 get_rs (state_machine::state_t state)
286 {
287 if (const allocation_state *astate = dyn_cast_allocation_state (state))
288 return astate->m_rs;
289 else
290 return RS_START;
291 }
292
293 /* Return true if STATE is an unchecked result from an allocator. */
294
295 static bool
296 unchecked_p (state_machine::state_t state)
297 {
298 return get_rs (state) == RS_UNCHECKED;
299 }
300
301 /* Return true if STATE is a non-null result from an allocator. */
302
303 static bool
304 nonnull_p (state_machine::state_t state)
305 {
306 return get_rs (state) == RS_NONNULL;
307 }
308
309 /* Return true if STATE is a value that has been passed to a deallocator. */
310
311 static bool
312 freed_p (state_machine::state_t state)
313 {
314 return get_rs (state) == RS_FREED;
315 }
316
317 /* Class for diagnostics relating to malloc_state_machine. */
318
319 class malloc_diagnostic : public pending_diagnostic
320 {
321 public:
322 malloc_diagnostic (const malloc_state_machine &sm, tree arg)
323 : m_sm (sm), m_arg (arg)
324 {}
325
326 bool subclass_equal_p (const pending_diagnostic &base_other) const OVERRIDE
327 {
328 return same_tree_p (m_arg, ((const malloc_diagnostic &)base_other).m_arg);
329 }
330
331 label_text describe_state_change (const evdesc::state_change &change)
332 OVERRIDE
333 {
334 if (change.m_old_state == m_sm.get_start_state ()
335 && unchecked_p (change.m_new_state))
336 // TODO: verify that it's the allocation stmt, not a copy
337 return label_text::borrow ("allocated here");
338 if (unchecked_p (change.m_old_state)
339 && nonnull_p (change.m_new_state))
340 {
341 if (change.m_expr)
342 return change.formatted_print ("assuming %qE is non-NULL",
343 change.m_expr);
344 else
345 return change.formatted_print ("assuming %qs is non-NULL",
346 "<unknown>");
347 }
348 if (change.m_new_state == m_sm.m_null)
349 {
350 if (unchecked_p (change.m_old_state))
351 {
352 if (change.m_expr)
353 return change.formatted_print ("assuming %qE is NULL",
354 change.m_expr);
355 else
356 return change.formatted_print ("assuming %qs is NULL",
357 "<unknown>");
358 }
359 else
360 {
361 if (change.m_expr)
362 return change.formatted_print ("%qE is NULL",
363 change.m_expr);
364 else
365 return change.formatted_print ("%qs is NULL",
366 "<unknown>");
367 }
368 }
369
370 return label_text ();
371 }
372
373 protected:
374 const malloc_state_machine &m_sm;
375 tree m_arg;
376 };
377
378 /* Concrete subclass for reporting mismatching allocator/deallocator
379 diagnostics. */
380
381 class mismatching_deallocation : public malloc_diagnostic
382 {
383 public:
384 mismatching_deallocation (const malloc_state_machine &sm, tree arg,
385 const api *expected_dealloc,
386 const api *actual_dealloc)
387 : malloc_diagnostic (sm, arg),
388 m_expected_dealloc (expected_dealloc),
389 m_actual_dealloc (actual_dealloc)
390 {}
391
392 const char *get_kind () const FINAL OVERRIDE
393 {
394 return "mismatching_deallocation";
395 }
396
397 bool emit (rich_location *rich_loc) FINAL OVERRIDE
398 {
399 auto_diagnostic_group d;
400 diagnostic_metadata m;
401 m.add_cwe (762); /* CWE-762: Mismatched Memory Management Routines. */
402 return warning_meta (rich_loc, m, OPT_Wanalyzer_mismatching_deallocation,
403 "%qE should have been deallocated with %qs"
404 " but was deallocated with %qs",
405 m_arg, m_expected_dealloc->m_dealloc_funcname,
406 m_actual_dealloc->m_dealloc_funcname);
407 }
408
409 label_text describe_state_change (const evdesc::state_change &change)
410 FINAL OVERRIDE
411 {
412 if (unchecked_p (change.m_new_state))
413 {
414 m_alloc_event = change.m_event_id;
415 return change.formatted_print ("allocated here"
416 " (expects deallocation with %qs)",
417 m_expected_dealloc->m_dealloc_funcname);
418 }
419 return malloc_diagnostic::describe_state_change (change);
420 }
421
422 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
423 {
424 if (m_alloc_event.known_p ())
425 return ev.formatted_print
426 ("deallocated with %qs here;"
427 " allocation at %@ expects deallocation with %qs",
428 m_actual_dealloc->m_dealloc_funcname, &m_alloc_event,
429 m_expected_dealloc->m_dealloc_funcname);
430 return ev.formatted_print ("deallocated with %qs here",
431 m_actual_dealloc->m_dealloc_funcname);
432 }
433
434 private:
435 diagnostic_event_id_t m_alloc_event;
436 const api *m_expected_dealloc;
437 const api *m_actual_dealloc;
438 };
439
440 /* Concrete subclass for reporting double-free diagnostics. */
441
442 class double_free : public malloc_diagnostic
443 {
444 public:
445 double_free (const malloc_state_machine &sm, tree arg, const char *funcname)
446 : malloc_diagnostic (sm, arg), m_funcname (funcname)
447 {}
448
449 const char *get_kind () const FINAL OVERRIDE { return "double_free"; }
450
451 bool emit (rich_location *rich_loc) FINAL OVERRIDE
452 {
453 auto_diagnostic_group d;
454 diagnostic_metadata m;
455 m.add_cwe (415); /* CWE-415: Double Free. */
456 return warning_meta (rich_loc, m, OPT_Wanalyzer_double_free,
457 "double-%<%s%> of %qE", m_funcname, m_arg);
458 }
459
460 label_text describe_state_change (const evdesc::state_change &change)
461 FINAL OVERRIDE
462 {
463 if (freed_p (change.m_new_state))
464 {
465 m_first_free_event = change.m_event_id;
466 return change.formatted_print ("first %qs here", m_funcname);
467 }
468 return malloc_diagnostic::describe_state_change (change);
469 }
470
471 label_text describe_call_with_state (const evdesc::call_with_state &info)
472 FINAL OVERRIDE
473 {
474 if (freed_p (info.m_state))
475 return info.formatted_print
476 ("passing freed pointer %qE in call to %qE from %qE",
477 info.m_expr, info.m_callee_fndecl, info.m_caller_fndecl);
478 return label_text ();
479 }
480
481 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
482 {
483 if (m_first_free_event.known_p ())
484 return ev.formatted_print ("second %qs here; first %qs was at %@",
485 m_funcname, m_funcname,
486 &m_first_free_event);
487 return ev.formatted_print ("second %qs here", m_funcname);
488 }
489
490 private:
491 diagnostic_event_id_t m_first_free_event;
492 const char *m_funcname;
493 };
494
495 /* Abstract subclass for describing possible bad uses of NULL.
496 Responsible for describing the call that could return NULL. */
497
498 class possible_null : public malloc_diagnostic
499 {
500 public:
501 possible_null (const malloc_state_machine &sm, tree arg)
502 : malloc_diagnostic (sm, arg)
503 {}
504
505 label_text describe_state_change (const evdesc::state_change &change)
506 FINAL OVERRIDE
507 {
508 if (change.m_old_state == m_sm.get_start_state ()
509 && unchecked_p (change.m_new_state))
510 {
511 m_origin_of_unchecked_event = change.m_event_id;
512 return label_text::borrow ("this call could return NULL");
513 }
514 return malloc_diagnostic::describe_state_change (change);
515 }
516
517 label_text describe_return_of_state (const evdesc::return_of_state &info)
518 FINAL OVERRIDE
519 {
520 if (unchecked_p (info.m_state))
521 return info.formatted_print ("possible return of NULL to %qE from %qE",
522 info.m_caller_fndecl, info.m_callee_fndecl);
523 return label_text ();
524 }
525
526 protected:
527 diagnostic_event_id_t m_origin_of_unchecked_event;
528 };
529
530 /* Concrete subclass for describing dereference of a possible NULL
531 value. */
532
533 class possible_null_deref : public possible_null
534 {
535 public:
536 possible_null_deref (const malloc_state_machine &sm, tree arg)
537 : possible_null (sm, arg)
538 {}
539
540 const char *get_kind () const FINAL OVERRIDE { return "possible_null_deref"; }
541
542 bool emit (rich_location *rich_loc) FINAL OVERRIDE
543 {
544 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
545 diagnostic_metadata m;
546 m.add_cwe (690);
547 return warning_meta (rich_loc, m,
548 OPT_Wanalyzer_possible_null_dereference,
549 "dereference of possibly-NULL %qE", m_arg);
550 }
551
552 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
553 {
554 if (m_origin_of_unchecked_event.known_p ())
555 return ev.formatted_print ("%qE could be NULL: unchecked value from %@",
556 ev.m_expr,
557 &m_origin_of_unchecked_event);
558 else
559 return ev.formatted_print ("%qE could be NULL", ev.m_expr);
560 }
561
562 };
563
564 /* Subroutine for use by possible_null_arg::emit and null_arg::emit.
565 Issue a note informing that the pertinent argument must be non-NULL. */
566
567 static void
568 inform_nonnull_attribute (tree fndecl, int arg_idx)
569 {
570 inform (DECL_SOURCE_LOCATION (fndecl),
571 "argument %u of %qD must be non-null",
572 arg_idx + 1, fndecl);
573 /* Ideally we would use the location of the parm and underline the
574 attribute also - but we don't have the location_t values at this point
575 in the middle-end.
576 For reference, the C and C++ FEs have get_fndecl_argument_location. */
577 }
578
579 /* Concrete subclass for describing passing a possibly-NULL value to a
580 function marked with __attribute__((nonnull)). */
581
582 class possible_null_arg : public possible_null
583 {
584 public:
585 possible_null_arg (const malloc_state_machine &sm, tree arg,
586 tree fndecl, int arg_idx)
587 : possible_null (sm, arg),
588 m_fndecl (fndecl), m_arg_idx (arg_idx)
589 {}
590
591 const char *get_kind () const FINAL OVERRIDE { return "possible_null_arg"; }
592
593 bool subclass_equal_p (const pending_diagnostic &base_other) const
594 {
595 const possible_null_arg &sub_other
596 = (const possible_null_arg &)base_other;
597 return (same_tree_p (m_arg, sub_other.m_arg)
598 && m_fndecl == sub_other.m_fndecl
599 && m_arg_idx == sub_other.m_arg_idx);
600 }
601
602
603 bool emit (rich_location *rich_loc) FINAL OVERRIDE
604 {
605 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
606 auto_diagnostic_group d;
607 diagnostic_metadata m;
608 m.add_cwe (690);
609 bool warned
610 = warning_meta (rich_loc, m, OPT_Wanalyzer_possible_null_argument,
611 "use of possibly-NULL %qE where non-null expected",
612 m_arg);
613 if (warned)
614 inform_nonnull_attribute (m_fndecl, m_arg_idx);
615 return warned;
616 }
617
618 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
619 {
620 if (m_origin_of_unchecked_event.known_p ())
621 return ev.formatted_print ("argument %u (%qE) from %@ could be NULL"
622 " where non-null expected",
623 m_arg_idx + 1, ev.m_expr,
624 &m_origin_of_unchecked_event);
625 else
626 return ev.formatted_print ("argument %u (%qE) could be NULL"
627 " where non-null expected",
628 m_arg_idx + 1, ev.m_expr);
629 }
630
631 private:
632 tree m_fndecl;
633 int m_arg_idx;
634 };
635
636 /* Concrete subclass for describing a dereference of a NULL value. */
637
638 class null_deref : public malloc_diagnostic
639 {
640 public:
641 null_deref (const malloc_state_machine &sm, tree arg)
642 : malloc_diagnostic (sm, arg) {}
643
644 const char *get_kind () const FINAL OVERRIDE { return "null_deref"; }
645
646 bool emit (rich_location *rich_loc) FINAL OVERRIDE
647 {
648 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
649 diagnostic_metadata m;
650 m.add_cwe (690);
651 return warning_meta (rich_loc, m,
652 OPT_Wanalyzer_null_dereference,
653 "dereference of NULL %qE", m_arg);
654 }
655
656 label_text describe_return_of_state (const evdesc::return_of_state &info)
657 FINAL OVERRIDE
658 {
659 if (info.m_state == m_sm.m_null)
660 return info.formatted_print ("return of NULL to %qE from %qE",
661 info.m_caller_fndecl, info.m_callee_fndecl);
662 return label_text ();
663 }
664
665 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
666 {
667 return ev.formatted_print ("dereference of NULL %qE", ev.m_expr);
668 }
669 };
670
671 /* Concrete subclass for describing passing a NULL value to a
672 function marked with __attribute__((nonnull)). */
673
674 class null_arg : public malloc_diagnostic
675 {
676 public:
677 null_arg (const malloc_state_machine &sm, tree arg,
678 tree fndecl, int arg_idx)
679 : malloc_diagnostic (sm, arg),
680 m_fndecl (fndecl), m_arg_idx (arg_idx)
681 {}
682
683 const char *get_kind () const FINAL OVERRIDE { return "null_arg"; }
684
685 bool subclass_equal_p (const pending_diagnostic &base_other) const
686 {
687 const null_arg &sub_other
688 = (const null_arg &)base_other;
689 return (same_tree_p (m_arg, sub_other.m_arg)
690 && m_fndecl == sub_other.m_fndecl
691 && m_arg_idx == sub_other.m_arg_idx);
692 }
693
694 bool emit (rich_location *rich_loc) FINAL OVERRIDE
695 {
696 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
697 auto_diagnostic_group d;
698 diagnostic_metadata m;
699 m.add_cwe (690);
700
701 bool warned;
702 if (zerop (m_arg))
703 warned = warning_meta (rich_loc, m, OPT_Wanalyzer_null_argument,
704 "use of NULL where non-null expected");
705 else
706 warned = warning_meta (rich_loc, m, OPT_Wanalyzer_null_argument,
707 "use of NULL %qE where non-null expected",
708 m_arg);
709 if (warned)
710 inform_nonnull_attribute (m_fndecl, m_arg_idx);
711 return warned;
712 }
713
714 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
715 {
716 if (zerop (ev.m_expr))
717 return ev.formatted_print ("argument %u NULL where non-null expected",
718 m_arg_idx + 1);
719 else
720 return ev.formatted_print ("argument %u (%qE) NULL"
721 " where non-null expected",
722 m_arg_idx + 1, ev.m_expr);
723 }
724
725 private:
726 tree m_fndecl;
727 int m_arg_idx;
728 };
729
730 class use_after_free : public malloc_diagnostic
731 {
732 public:
733 use_after_free (const malloc_state_machine &sm, tree arg,
734 const api *a)
735 : malloc_diagnostic (sm, arg), m_api (a)
736 {}
737
738 const char *get_kind () const FINAL OVERRIDE { return "use_after_free"; }
739
740 bool emit (rich_location *rich_loc) FINAL OVERRIDE
741 {
742 /* CWE-416: Use After Free. */
743 diagnostic_metadata m;
744 m.add_cwe (416);
745 return warning_meta (rich_loc, m, OPT_Wanalyzer_use_after_free,
746 "use after %<%s%> of %qE",
747 m_api->m_dealloc_funcname, m_arg);
748 }
749
750 label_text describe_state_change (const evdesc::state_change &change)
751 FINAL OVERRIDE
752 {
753 if (freed_p (change.m_new_state))
754 {
755 m_free_event = change.m_event_id;
756 switch (m_api->m_wording)
757 {
758 default:
759 gcc_unreachable ();
760 case WORDING_FREED:
761 return label_text::borrow ("freed here");
762 case WORDING_DELETED:
763 return label_text::borrow ("deleted here");
764 }
765 }
766 return malloc_diagnostic::describe_state_change (change);
767 }
768
769 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
770 {
771 const char *funcname = m_api->m_dealloc_funcname;
772 if (m_free_event.known_p ())
773 switch (m_api->m_wording)
774 {
775 default:
776 gcc_unreachable ();
777 case WORDING_FREED:
778 return ev.formatted_print ("use after %<%s%> of %qE; freed at %@",
779 funcname, ev.m_expr, &m_free_event);
780 case WORDING_DELETED:
781 return ev.formatted_print ("use after %<%s%> of %qE; deleted at %@",
782 funcname, ev.m_expr, &m_free_event);
783 }
784 else
785 return ev.formatted_print ("use after %<%s%> of %qE",
786 funcname, ev.m_expr);
787 }
788
789 private:
790 diagnostic_event_id_t m_free_event;
791 const api *m_api;
792 };
793
794 class malloc_leak : public malloc_diagnostic
795 {
796 public:
797 malloc_leak (const malloc_state_machine &sm, tree arg)
798 : malloc_diagnostic (sm, arg) {}
799
800 const char *get_kind () const FINAL OVERRIDE { return "malloc_leak"; }
801
802 bool emit (rich_location *rich_loc) FINAL OVERRIDE
803 {
804 diagnostic_metadata m;
805 m.add_cwe (401);
806 if (m_arg)
807 return warning_meta (rich_loc, m, OPT_Wanalyzer_malloc_leak,
808 "leak of %qE", m_arg);
809 else
810 return warning_meta (rich_loc, m, OPT_Wanalyzer_malloc_leak,
811 "leak of %qs", "<unknown>");
812 }
813
814 label_text describe_state_change (const evdesc::state_change &change)
815 FINAL OVERRIDE
816 {
817 if (unchecked_p (change.m_new_state))
818 {
819 m_alloc_event = change.m_event_id;
820 return label_text::borrow ("allocated here");
821 }
822 return malloc_diagnostic::describe_state_change (change);
823 }
824
825 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
826 {
827 if (ev.m_expr)
828 {
829 if (m_alloc_event.known_p ())
830 return ev.formatted_print ("%qE leaks here; was allocated at %@",
831 ev.m_expr, &m_alloc_event);
832 else
833 return ev.formatted_print ("%qE leaks here", ev.m_expr);
834 }
835 else
836 {
837 if (m_alloc_event.known_p ())
838 return ev.formatted_print ("%qs leaks here; was allocated at %@",
839 "<unknown>", &m_alloc_event);
840 else
841 return ev.formatted_print ("%qs leaks here", "<unknown>");
842 }
843 }
844
845 private:
846 diagnostic_event_id_t m_alloc_event;
847 };
848
849 class free_of_non_heap : public malloc_diagnostic
850 {
851 public:
852 free_of_non_heap (const malloc_state_machine &sm, tree arg,
853 const char *funcname)
854 : malloc_diagnostic (sm, arg), m_funcname (funcname), m_kind (KIND_UNKNOWN)
855 {
856 }
857
858 const char *get_kind () const FINAL OVERRIDE { return "free_of_non_heap"; }
859
860 bool subclass_equal_p (const pending_diagnostic &base_other) const
861 FINAL OVERRIDE
862 {
863 const free_of_non_heap &other = (const free_of_non_heap &)base_other;
864 return (same_tree_p (m_arg, other.m_arg) && m_kind == other.m_kind);
865 }
866
867 bool emit (rich_location *rich_loc) FINAL OVERRIDE
868 {
869 auto_diagnostic_group d;
870 diagnostic_metadata m;
871 m.add_cwe (590); /* CWE-590: Free of Memory not on the Heap. */
872 switch (m_kind)
873 {
874 default:
875 gcc_unreachable ();
876 case KIND_UNKNOWN:
877 return warning_meta (rich_loc, m, OPT_Wanalyzer_free_of_non_heap,
878 "%<%s%> of %qE which points to memory"
879 " not on the heap",
880 m_funcname, m_arg);
881 break;
882 case KIND_ALLOCA:
883 return warning_meta (rich_loc, m, OPT_Wanalyzer_free_of_non_heap,
884 "%<%s%> of memory allocated on the stack by"
885 " %qs (%qE) will corrupt the heap",
886 m_funcname, "alloca", m_arg);
887 break;
888 }
889 }
890
891 label_text describe_state_change (const evdesc::state_change &change)
892 FINAL OVERRIDE
893 {
894 /* Attempt to reconstruct what kind of pointer it is.
895 (It seems neater for this to be a part of the state, though). */
896 if (TREE_CODE (change.m_expr) == SSA_NAME)
897 {
898 gimple *def_stmt = SSA_NAME_DEF_STMT (change.m_expr);
899 if (gcall *call = dyn_cast <gcall *> (def_stmt))
900 {
901 if (is_special_named_call_p (call, "alloca", 1)
902 || is_special_named_call_p (call, "__builtin_alloca", 1))
903 {
904 m_kind = KIND_ALLOCA;
905 return label_text::borrow
906 ("memory is allocated on the stack here");
907 }
908 }
909 }
910 return label_text::borrow ("pointer is from here");
911 }
912
913 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
914 {
915 return ev.formatted_print ("call to %qs here", m_funcname);
916 }
917
918 private:
919 enum kind
920 {
921 KIND_UNKNOWN,
922 KIND_ALLOCA
923 };
924 const char *m_funcname;
925 enum kind m_kind;
926 };
927
928 /* struct allocation_state : public state_machine::state. */
929
930 /* Implementation of state_machine::state::dump_to_pp vfunc
931 for allocation_state: append the API that this allocation is
932 associated with. */
933
934 void
935 allocation_state::dump_to_pp (pretty_printer *pp) const
936 {
937 state_machine::state::dump_to_pp (pp);
938 if (m_api)
939 pp_printf (pp, " (%s)", m_api->m_name);
940 }
941
942 /* Given a allocation_state for an api, get the "nonnull" state
943 for the corresponding allocator. */
944
945 const allocation_state *
946 allocation_state::get_nonnull () const
947 {
948 gcc_assert (m_api);
949 return as_a_allocation_state (m_api->m_nonnull);
950 }
951
952 /* malloc_state_machine's ctor. */
953
954 malloc_state_machine::malloc_state_machine (logger *logger)
955 : state_machine ("malloc", logger),
956 m_malloc (this, "malloc", "free", WORDING_FREED),
957 m_scalar_new (this, "new", "delete", WORDING_DELETED),
958 m_vector_new (this, "new[]", "delete[]", WORDING_DELETED)
959 {
960 gcc_assert (m_start->get_id () == 0);
961 m_null = add_state ("null", RS_FREED, NULL);
962 m_non_heap = add_state ("non-heap", RS_NON_HEAP, NULL);
963 m_stop = add_state ("stop", RS_STOP, NULL);
964 }
965
966 state_machine::state_t
967 malloc_state_machine::add_state (const char *name, enum resource_state rs,
968 const api *a)
969 {
970 return add_custom_state (new allocation_state (name, alloc_state_id (),
971 rs, a));
972 }
973
974 /* Implementation of state_machine::on_stmt vfunc for malloc_state_machine. */
975
976 bool
977 malloc_state_machine::on_stmt (sm_context *sm_ctxt,
978 const supernode *node,
979 const gimple *stmt) const
980 {
981 if (const gcall *call = dyn_cast <const gcall *> (stmt))
982 if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call))
983 {
984 if (is_named_call_p (callee_fndecl, "malloc", call, 1)
985 || is_named_call_p (callee_fndecl, "calloc", call, 2)
986 || is_std_named_call_p (callee_fndecl, "malloc", call, 1)
987 || is_std_named_call_p (callee_fndecl, "calloc", call, 2)
988 || is_named_call_p (callee_fndecl, "__builtin_malloc", call, 1)
989 || is_named_call_p (callee_fndecl, "__builtin_calloc", call, 2)
990 || is_named_call_p (callee_fndecl, "strdup", call, 1)
991 || is_named_call_p (callee_fndecl, "strndup", call, 2))
992 {
993 on_allocator_call (sm_ctxt, call, m_malloc);
994 return true;
995 }
996
997 if (is_named_call_p (callee_fndecl, "operator new", call, 1))
998 on_allocator_call (sm_ctxt, call, m_scalar_new);
999 else if (is_named_call_p (callee_fndecl, "operator new []", call, 1))
1000 on_allocator_call (sm_ctxt, call, m_vector_new);
1001 else if (is_named_call_p (callee_fndecl, "operator delete", call, 1)
1002 || is_named_call_p (callee_fndecl, "operator delete", call, 2))
1003 {
1004 on_deallocator_call (sm_ctxt, node, call, m_scalar_new);
1005 return true;
1006 }
1007 else if (is_named_call_p (callee_fndecl, "operator delete []", call, 1))
1008 {
1009 on_deallocator_call (sm_ctxt, node, call, m_vector_new);
1010 return true;
1011 }
1012
1013 if (is_named_call_p (callee_fndecl, "alloca", call, 1)
1014 || is_named_call_p (callee_fndecl, "__builtin_alloca", call, 1))
1015 {
1016 tree lhs = gimple_call_lhs (call);
1017 if (lhs)
1018 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_non_heap);
1019 return true;
1020 }
1021
1022 if (is_named_call_p (callee_fndecl, "free", call, 1)
1023 || is_std_named_call_p (callee_fndecl, "free", call, 1)
1024 || is_named_call_p (callee_fndecl, "__builtin_free", call, 1))
1025 {
1026 on_deallocator_call (sm_ctxt, node, call, m_malloc);
1027 return true;
1028 }
1029
1030 /* Handle "__attribute__((nonnull))". */
1031 {
1032 tree fntype = TREE_TYPE (callee_fndecl);
1033 bitmap nonnull_args = get_nonnull_args (fntype);
1034 if (nonnull_args)
1035 {
1036 for (unsigned i = 0; i < gimple_call_num_args (stmt); i++)
1037 {
1038 tree arg = gimple_call_arg (stmt, i);
1039 if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
1040 continue;
1041 /* If we have a nonnull-args, and either all pointers, or just
1042 the specified pointers. */
1043 if (bitmap_empty_p (nonnull_args)
1044 || bitmap_bit_p (nonnull_args, i))
1045 {
1046 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1047 state_t state = sm_ctxt->get_state (stmt, arg);
1048 /* Can't use a switch as the states are non-const. */
1049 if (unchecked_p (state))
1050 {
1051 sm_ctxt->warn (node, stmt, arg,
1052 new possible_null_arg (*this, diag_arg,
1053 callee_fndecl,
1054 i));
1055 const allocation_state *astate
1056 = as_a_allocation_state (state);
1057 sm_ctxt->set_next_state (stmt, arg,
1058 astate->get_nonnull ());
1059 }
1060 else if (state == m_null)
1061 {
1062 sm_ctxt->warn (node, stmt, arg,
1063 new null_arg (*this, diag_arg,
1064 callee_fndecl, i));
1065 sm_ctxt->set_next_state (stmt, arg, m_stop);
1066 }
1067 }
1068 }
1069 BITMAP_FREE (nonnull_args);
1070 }
1071 }
1072 }
1073
1074 if (tree lhs = sm_ctxt->is_zero_assignment (stmt))
1075 if (any_pointer_p (lhs))
1076 on_zero_assignment (sm_ctxt, stmt,lhs);
1077
1078 /* If we have "LHS = &EXPR;" and EXPR is something other than a MEM_REF,
1079 transition LHS from start to non_heap.
1080 Doing it for ADDR_EXPR(MEM_REF()) is likely wrong, and can lead to
1081 unbounded chains of unmergeable sm-state on pointer arithmetic in loops
1082 when optimization is enabled. */
1083 if (const gassign *assign_stmt = dyn_cast <const gassign *> (stmt))
1084 {
1085 enum tree_code op = gimple_assign_rhs_code (assign_stmt);
1086 if (op == ADDR_EXPR)
1087 {
1088 tree lhs = gimple_assign_lhs (assign_stmt);
1089 if (lhs)
1090 {
1091 tree addr_expr = gimple_assign_rhs1 (assign_stmt);
1092 if (TREE_CODE (TREE_OPERAND (addr_expr, 0)) != MEM_REF)
1093 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_non_heap);
1094 }
1095 }
1096 }
1097
1098 /* Handle dereferences. */
1099 for (unsigned i = 0; i < gimple_num_ops (stmt); i++)
1100 {
1101 tree op = gimple_op (stmt, i);
1102 if (!op)
1103 continue;
1104 if (TREE_CODE (op) == COMPONENT_REF)
1105 op = TREE_OPERAND (op, 0);
1106
1107 if (TREE_CODE (op) == MEM_REF)
1108 {
1109 tree arg = TREE_OPERAND (op, 0);
1110 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1111
1112 state_t state = sm_ctxt->get_state (stmt, arg);
1113 if (unchecked_p (state))
1114 {
1115 sm_ctxt->warn (node, stmt, arg,
1116 new possible_null_deref (*this, diag_arg));
1117 const allocation_state *astate = as_a_allocation_state (state);
1118 sm_ctxt->set_next_state (stmt, arg, astate->get_nonnull ());
1119 }
1120 else if (state == m_null)
1121 {
1122 sm_ctxt->warn (node, stmt, arg,
1123 new null_deref (*this, diag_arg));
1124 sm_ctxt->set_next_state (stmt, arg, m_stop);
1125 }
1126 else if (freed_p (state))
1127 {
1128 const allocation_state *astate = as_a_allocation_state (state);
1129 sm_ctxt->warn (node, stmt, arg,
1130 new use_after_free (*this, diag_arg,
1131 astate->m_api));
1132 sm_ctxt->set_next_state (stmt, arg, m_stop);
1133 }
1134 }
1135 }
1136 return false;
1137 }
1138
1139 /* Handle a call to an allocator. */
1140
1141 void
1142 malloc_state_machine::on_allocator_call (sm_context *sm_ctxt,
1143 const gcall *call,
1144 const api &ap) const
1145 {
1146 tree lhs = gimple_call_lhs (call);
1147 if (lhs)
1148 {
1149 if (sm_ctxt->get_state (call, lhs) == m_start)
1150 sm_ctxt->set_next_state (call, lhs, ap.m_unchecked);
1151 }
1152 else
1153 {
1154 /* TODO: report leak. */
1155 }
1156 }
1157
1158 void
1159 malloc_state_machine::on_deallocator_call (sm_context *sm_ctxt,
1160 const supernode *node,
1161 const gcall *call,
1162 const api &ap) const
1163 {
1164 tree arg = gimple_call_arg (call, 0);
1165 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1166
1167 state_t state = sm_ctxt->get_state (call, arg);
1168
1169 /* start/unchecked/nonnull -> freed. */
1170 if (state == m_start)
1171 sm_ctxt->set_next_state (call, arg, ap.m_freed);
1172 else if (unchecked_p (state) || nonnull_p (state))
1173 {
1174 const allocation_state *astate = as_a_allocation_state (state);
1175
1176 if (astate->m_api != &ap)
1177 {
1178 /* Wrong allocator. */
1179 pending_diagnostic *d
1180 = new mismatching_deallocation (*this, diag_arg,
1181 astate->m_api, &ap);
1182 sm_ctxt->warn (node, call, arg, d);
1183 }
1184 sm_ctxt->set_next_state (call, arg, ap.m_freed);
1185 }
1186
1187 /* Keep state "null" as-is, rather than transitioning to "freed";
1188 we don't want to complain about double-free of NULL. */
1189
1190 else if (state == ap.m_freed)
1191 {
1192 /* freed -> stop, with warning. */
1193 sm_ctxt->warn (node, call, arg,
1194 new double_free (*this, diag_arg,
1195 ap.m_dealloc_funcname));
1196 sm_ctxt->set_next_state (call, arg, m_stop);
1197 }
1198 else if (state == m_non_heap)
1199 {
1200 /* non-heap -> stop, with warning. */
1201 sm_ctxt->warn (node, call, arg,
1202 new free_of_non_heap (*this, diag_arg,
1203 ap.m_dealloc_funcname));
1204 sm_ctxt->set_next_state (call, arg, m_stop);
1205 }
1206 }
1207
1208 /* Implementation of state_machine::on_phi vfunc for malloc_state_machine. */
1209
1210 void
1211 malloc_state_machine::on_phi (sm_context *sm_ctxt,
1212 const supernode *node ATTRIBUTE_UNUSED,
1213 const gphi *phi,
1214 tree rhs) const
1215 {
1216 if (zerop (rhs))
1217 {
1218 tree lhs = gimple_phi_result (phi);
1219 on_zero_assignment (sm_ctxt, phi, lhs);
1220 }
1221 }
1222
1223 /* Implementation of state_machine::on_condition vfunc for malloc_state_machine.
1224 Potentially transition state 'unchecked' to 'nonnull' or to 'null'. */
1225
1226 void
1227 malloc_state_machine::on_condition (sm_context *sm_ctxt,
1228 const supernode *node ATTRIBUTE_UNUSED,
1229 const gimple *stmt,
1230 tree lhs,
1231 enum tree_code op,
1232 tree rhs) const
1233 {
1234 if (!zerop (rhs))
1235 return;
1236
1237 if (!any_pointer_p (lhs))
1238 return;
1239 if (!any_pointer_p (rhs))
1240 return;
1241
1242 if (op == NE_EXPR)
1243 {
1244 log ("got 'ARG != 0' match");
1245 state_t s = sm_ctxt->get_state (stmt, lhs);
1246 if (unchecked_p (s))
1247 {
1248 const allocation_state *astate = as_a_allocation_state (s);
1249 sm_ctxt->set_next_state (stmt, lhs, astate->get_nonnull ());
1250 }
1251 }
1252 else if (op == EQ_EXPR)
1253 {
1254 log ("got 'ARG == 0' match");
1255 state_t s = sm_ctxt->get_state (stmt, lhs);
1256 if (unchecked_p (s))
1257 sm_ctxt->set_next_state (stmt, lhs, m_null);
1258 }
1259 }
1260
1261 /* Implementation of state_machine::can_purge_p vfunc for malloc_state_machine.
1262 Don't allow purging of pointers in state 'unchecked' or 'nonnull'
1263 (to avoid false leak reports). */
1264
1265 bool
1266 malloc_state_machine::can_purge_p (state_t s) const
1267 {
1268 enum resource_state rs = get_rs (s);
1269 return rs != RS_UNCHECKED && rs != RS_NONNULL;
1270 }
1271
1272 /* Implementation of state_machine::on_leak vfunc for malloc_state_machine
1273 (for complaining about leaks of pointers in state 'unchecked' and
1274 'nonnull'). */
1275
1276 pending_diagnostic *
1277 malloc_state_machine::on_leak (tree var) const
1278 {
1279 return new malloc_leak (*this, var);
1280 }
1281
1282 /* Implementation of state_machine::reset_when_passed_to_unknown_fn_p vfunc
1283 for malloc_state_machine. */
1284
1285 bool
1286 malloc_state_machine::reset_when_passed_to_unknown_fn_p (state_t s,
1287 bool is_mutable) const
1288 {
1289 /* An on-stack ptr doesn't stop being stack-allocated when passed to an
1290 unknown fn. */
1291 if (s == m_non_heap)
1292 return false;
1293
1294 /* Otherwise, pointers passed as non-const can be freed. */
1295 return is_mutable;
1296 }
1297
1298 /* Shared logic for handling GIMPLE_ASSIGNs and GIMPLE_PHIs that
1299 assign zero to LHS. */
1300
1301 void
1302 malloc_state_machine::on_zero_assignment (sm_context *sm_ctxt,
1303 const gimple *stmt,
1304 tree lhs) const
1305 {
1306 state_t s = sm_ctxt->get_state (stmt, lhs);
1307 enum resource_state rs = get_rs (s);
1308 if (rs == RS_START
1309 || rs == RS_UNCHECKED
1310 || rs == RS_NONNULL
1311 || rs == RS_FREED)
1312 sm_ctxt->set_next_state (stmt, lhs, m_null);
1313 }
1314
1315 } // anonymous namespace
1316
1317 /* Internal interface to this file. */
1318
1319 state_machine *
1320 make_malloc_state_machine (logger *logger)
1321 {
1322 return new malloc_state_machine (logger);
1323 }
1324
1325 } // namespace ana
1326
1327 #endif /* #if ENABLE_ANALYZER */