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>.
5 This file is part of GCC.
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)
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.
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/>. */
23 #include "coretypes.h"
26 #include "basic-block.h"
30 #include "diagnostic-path.h"
31 #include "diagnostic-metadata.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"
40 #include "analyzer/call-string.h"
41 #include "analyzer/program-point.h"
42 #include "analyzer/store.h"
43 #include "analyzer/region-model.h"
52 class malloc_state_machine
;
54 /* An enum for discriminating between different kinds of allocation_state. */
58 /* States that are independent of api. */
60 /* The start state. */
63 /* State for a pointer that's known to be NULL. */
66 /* State for a pointer that's known to not be on the heap (e.g. to a local
70 /* Stop state, for pointers we don't want to track any more. */
73 /* States that relate to a specific api. */
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. */
80 /* State for a pointer returned from the api's allocator,
81 known to be non-NULL. */
84 /* State for a pointer passed to the api's deallocator. */
88 /* Custom state subclass, which can optionally refer to an an api. */
90 struct allocation_state
: public state_machine::state
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
)
97 void dump_to_pp (pretty_printer
*pp
) const FINAL OVERRIDE
;
99 const allocation_state
*get_nonnull () const;
101 enum resource_state m_rs
;
105 /* An enum for choosing which wording to use in various diagnostics
106 when describing deallocations. */
114 /* Represents a particular family of API calls for allocating/deallocating
115 heap memory that should be matched e.g.
118 - vector new[]/delete[]
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. */
132 api (malloc_state_machine
*sm
,
134 const char *dealloc_funcname
,
135 enum wording wording
);
137 /* An internal name for identifying this API in dumps. */
140 /* The name of the deallocation function, for use in diagnostics. */
141 const char *m_dealloc_funcname
;
143 /* Which wording to use in diagnostics. */
144 enum wording m_wording
;
146 /* Pointers to api-specific states.
147 These states are owned by the state_machine base class. */
149 /* State for an unchecked result from this api's allocator. */
150 state_machine::state_t m_unchecked
;
152 /* State for a known non-NULL result from this apis's allocator. */
153 state_machine::state_t m_nonnull
;
155 /* State for a value passed to this api's deallocator. */
156 state_machine::state_t m_freed
;
159 /* A state machine for detecting misuses of the malloc/free API.
161 See sm-malloc.dot for an overview (keep this in-sync with that file). */
163 class malloc_state_machine
: public state_machine
166 typedef allocation_state custom_data_t
;
168 malloc_state_machine (logger
*logger
);
171 add_state (const char *name
, enum resource_state rs
, const api
*a
);
173 bool inherited_state_p () const FINAL OVERRIDE
{ return false; }
175 state_machine::state_t
176 get_default_state (const svalue
*sval
) const FINAL OVERRIDE
178 if (tree cst
= sval
->maybe_get_constant ())
183 if (const region_svalue
*ptr
= sval
->dyn_cast_region_svalue ())
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
)
194 bool on_stmt (sm_context
*sm_ctxt
,
195 const supernode
*node
,
196 const gimple
*stmt
) const FINAL OVERRIDE
;
198 void on_phi (sm_context
*sm_ctxt
,
199 const supernode
*node
,
201 tree rhs
) const FINAL OVERRIDE
;
203 void on_condition (sm_context
*sm_ctxt
,
204 const supernode
*node
,
208 tree rhs
) const FINAL OVERRIDE
;
210 bool can_purge_p (state_t s
) const FINAL OVERRIDE
;
211 pending_diagnostic
*on_leak (tree var
) const FINAL OVERRIDE
;
213 bool reset_when_passed_to_unknown_fn_p (state_t s
,
214 bool is_mutable
) const FINAL OVERRIDE
;
220 /* States that are independent of api. */
222 /* State for a pointer that's known to be NULL. */
225 /* State for a pointer that's known to not be on the heap (e.g. to a local
227 state_t m_non_heap
; // TODO: or should this be a different state machine?
228 // or do we need child values etc?
230 /* Stop state, for pointers we don't want to track any more. */
234 void on_allocator_call (sm_context
*sm_ctxt
,
236 const api
&ap
) const;
237 void on_deallocator_call (sm_context
*sm_ctxt
,
238 const supernode
*node
,
240 const api
&ap
) const;
241 void on_zero_assignment (sm_context
*sm_ctxt
,
248 api::api (malloc_state_machine
*sm
,
250 const char *dealloc_funcname
,
251 enum wording wording
)
253 m_dealloc_funcname (dealloc_funcname
),
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))
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. */
264 static const allocation_state
*
265 dyn_cast_allocation_state (state_machine::state_t state
)
267 if (state
->get_id () == 0)
269 return static_cast <const allocation_state
*> (state
);
272 /* Return STATE cast to the custom state subclass, for a state that is
273 already known to not be the start state . */
275 static const allocation_state
*
276 as_a_allocation_state (state_machine::state_t state
)
278 gcc_assert (state
->get_id () != 0);
279 return static_cast <const allocation_state
*> (state
);
282 /* Get the resource_state for STATE. */
284 static enum resource_state
285 get_rs (state_machine::state_t state
)
287 if (const allocation_state
*astate
= dyn_cast_allocation_state (state
))
293 /* Return true if STATE is an unchecked result from an allocator. */
296 unchecked_p (state_machine::state_t state
)
298 return get_rs (state
) == RS_UNCHECKED
;
301 /* Return true if STATE is a non-null result from an allocator. */
304 nonnull_p (state_machine::state_t state
)
306 return get_rs (state
) == RS_NONNULL
;
309 /* Return true if STATE is a value that has been passed to a deallocator. */
312 freed_p (state_machine::state_t state
)
314 return get_rs (state
) == RS_FREED
;
317 /* Class for diagnostics relating to malloc_state_machine. */
319 class malloc_diagnostic
: public pending_diagnostic
322 malloc_diagnostic (const malloc_state_machine
&sm
, tree arg
)
323 : m_sm (sm
), m_arg (arg
)
326 bool subclass_equal_p (const pending_diagnostic
&base_other
) const OVERRIDE
328 return same_tree_p (m_arg
, ((const malloc_diagnostic
&)base_other
).m_arg
);
331 label_text
describe_state_change (const evdesc::state_change
&change
)
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
))
342 return change
.formatted_print ("assuming %qE is non-NULL",
345 return change
.formatted_print ("assuming %qs is non-NULL",
348 if (change
.m_new_state
== m_sm
.m_null
)
350 if (unchecked_p (change
.m_old_state
))
353 return change
.formatted_print ("assuming %qE is NULL",
356 return change
.formatted_print ("assuming %qs is NULL",
362 return change
.formatted_print ("%qE is NULL",
365 return change
.formatted_print ("%qs is NULL",
370 return label_text ();
374 const malloc_state_machine
&m_sm
;
378 /* Concrete subclass for reporting mismatching allocator/deallocator
381 class mismatching_deallocation
: public malloc_diagnostic
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
)
392 const char *get_kind () const FINAL OVERRIDE
394 return "mismatching_deallocation";
397 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
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
);
409 label_text
describe_state_change (const evdesc::state_change
&change
)
412 if (unchecked_p (change
.m_new_state
))
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
);
419 return malloc_diagnostic::describe_state_change (change
);
422 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
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
);
435 diagnostic_event_id_t m_alloc_event
;
436 const api
*m_expected_dealloc
;
437 const api
*m_actual_dealloc
;
440 /* Concrete subclass for reporting double-free diagnostics. */
442 class double_free
: public malloc_diagnostic
445 double_free (const malloc_state_machine
&sm
, tree arg
, const char *funcname
)
446 : malloc_diagnostic (sm
, arg
), m_funcname (funcname
)
449 const char *get_kind () const FINAL OVERRIDE
{ return "double_free"; }
451 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
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
);
460 label_text
describe_state_change (const evdesc::state_change
&change
)
463 if (freed_p (change
.m_new_state
))
465 m_first_free_event
= change
.m_event_id
;
466 return change
.formatted_print ("first %qs here", m_funcname
);
468 return malloc_diagnostic::describe_state_change (change
);
471 label_text
describe_call_with_state (const evdesc::call_with_state
&info
)
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 ();
481 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
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
);
491 diagnostic_event_id_t m_first_free_event
;
492 const char *m_funcname
;
495 /* Abstract subclass for describing possible bad uses of NULL.
496 Responsible for describing the call that could return NULL. */
498 class possible_null
: public malloc_diagnostic
501 possible_null (const malloc_state_machine
&sm
, tree arg
)
502 : malloc_diagnostic (sm
, arg
)
505 label_text
describe_state_change (const evdesc::state_change
&change
)
508 if (change
.m_old_state
== m_sm
.get_start_state ()
509 && unchecked_p (change
.m_new_state
))
511 m_origin_of_unchecked_event
= change
.m_event_id
;
512 return label_text::borrow ("this call could return NULL");
514 return malloc_diagnostic::describe_state_change (change
);
517 label_text
describe_return_of_state (const evdesc::return_of_state
&info
)
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 ();
527 diagnostic_event_id_t m_origin_of_unchecked_event
;
530 /* Concrete subclass for describing dereference of a possible NULL
533 class possible_null_deref
: public possible_null
536 possible_null_deref (const malloc_state_machine
&sm
, tree arg
)
537 : possible_null (sm
, arg
)
540 const char *get_kind () const FINAL OVERRIDE
{ return "possible_null_deref"; }
542 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
544 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
545 diagnostic_metadata m
;
547 return warning_meta (rich_loc
, m
,
548 OPT_Wanalyzer_possible_null_dereference
,
549 "dereference of possibly-NULL %qE", m_arg
);
552 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
554 if (m_origin_of_unchecked_event
.known_p ())
555 return ev
.formatted_print ("%qE could be NULL: unchecked value from %@",
557 &m_origin_of_unchecked_event
);
559 return ev
.formatted_print ("%qE could be NULL", ev
.m_expr
);
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. */
568 inform_nonnull_attribute (tree fndecl
, int arg_idx
)
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
576 For reference, the C and C++ FEs have get_fndecl_argument_location. */
579 /* Concrete subclass for describing passing a possibly-NULL value to a
580 function marked with __attribute__((nonnull)). */
582 class possible_null_arg
: public possible_null
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
)
591 const char *get_kind () const FINAL OVERRIDE
{ return "possible_null_arg"; }
593 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
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
);
603 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
605 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
606 auto_diagnostic_group d
;
607 diagnostic_metadata m
;
610 = warning_meta (rich_loc
, m
, OPT_Wanalyzer_possible_null_argument
,
611 "use of possibly-NULL %qE where non-null expected",
614 inform_nonnull_attribute (m_fndecl
, m_arg_idx
);
618 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
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
);
626 return ev
.formatted_print ("argument %u (%qE) could be NULL"
627 " where non-null expected",
628 m_arg_idx
+ 1, ev
.m_expr
);
636 /* Concrete subclass for describing a dereference of a NULL value. */
638 class null_deref
: public malloc_diagnostic
641 null_deref (const malloc_state_machine
&sm
, tree arg
)
642 : malloc_diagnostic (sm
, arg
) {}
644 const char *get_kind () const FINAL OVERRIDE
{ return "null_deref"; }
646 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
648 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
649 diagnostic_metadata m
;
651 return warning_meta (rich_loc
, m
,
652 OPT_Wanalyzer_null_dereference
,
653 "dereference of NULL %qE", m_arg
);
656 label_text
describe_return_of_state (const evdesc::return_of_state
&info
)
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 ();
665 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
667 return ev
.formatted_print ("dereference of NULL %qE", ev
.m_expr
);
671 /* Concrete subclass for describing passing a NULL value to a
672 function marked with __attribute__((nonnull)). */
674 class null_arg
: public malloc_diagnostic
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
)
683 const char *get_kind () const FINAL OVERRIDE
{ return "null_arg"; }
685 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
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
);
694 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
696 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
697 auto_diagnostic_group d
;
698 diagnostic_metadata m
;
703 warned
= warning_meta (rich_loc
, m
, OPT_Wanalyzer_null_argument
,
704 "use of NULL where non-null expected");
706 warned
= warning_meta (rich_loc
, m
, OPT_Wanalyzer_null_argument
,
707 "use of NULL %qE where non-null expected",
710 inform_nonnull_attribute (m_fndecl
, m_arg_idx
);
714 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
716 if (zerop (ev
.m_expr
))
717 return ev
.formatted_print ("argument %u NULL where non-null expected",
720 return ev
.formatted_print ("argument %u (%qE) NULL"
721 " where non-null expected",
722 m_arg_idx
+ 1, ev
.m_expr
);
730 class use_after_free
: public malloc_diagnostic
733 use_after_free (const malloc_state_machine
&sm
, tree arg
,
735 : malloc_diagnostic (sm
, arg
), m_api (a
)
738 const char *get_kind () const FINAL OVERRIDE
{ return "use_after_free"; }
740 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
742 /* CWE-416: Use After Free. */
743 diagnostic_metadata m
;
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
);
750 label_text
describe_state_change (const evdesc::state_change
&change
)
753 if (freed_p (change
.m_new_state
))
755 m_free_event
= change
.m_event_id
;
756 switch (m_api
->m_wording
)
761 return label_text::borrow ("freed here");
762 case WORDING_DELETED
:
763 return label_text::borrow ("deleted here");
766 return malloc_diagnostic::describe_state_change (change
);
769 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
771 const char *funcname
= m_api
->m_dealloc_funcname
;
772 if (m_free_event
.known_p ())
773 switch (m_api
->m_wording
)
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
);
785 return ev
.formatted_print ("use after %<%s%> of %qE",
786 funcname
, ev
.m_expr
);
790 diagnostic_event_id_t m_free_event
;
794 class malloc_leak
: public malloc_diagnostic
797 malloc_leak (const malloc_state_machine
&sm
, tree arg
)
798 : malloc_diagnostic (sm
, arg
) {}
800 const char *get_kind () const FINAL OVERRIDE
{ return "malloc_leak"; }
802 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
804 diagnostic_metadata m
;
807 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_malloc_leak
,
808 "leak of %qE", m_arg
);
810 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_malloc_leak
,
811 "leak of %qs", "<unknown>");
814 label_text
describe_state_change (const evdesc::state_change
&change
)
817 if (unchecked_p (change
.m_new_state
))
819 m_alloc_event
= change
.m_event_id
;
820 return label_text::borrow ("allocated here");
822 return malloc_diagnostic::describe_state_change (change
);
825 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
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
);
833 return ev
.formatted_print ("%qE leaks here", ev
.m_expr
);
837 if (m_alloc_event
.known_p ())
838 return ev
.formatted_print ("%qs leaks here; was allocated at %@",
839 "<unknown>", &m_alloc_event
);
841 return ev
.formatted_print ("%qs leaks here", "<unknown>");
846 diagnostic_event_id_t m_alloc_event
;
849 class free_of_non_heap
: public malloc_diagnostic
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
)
858 const char *get_kind () const FINAL OVERRIDE
{ return "free_of_non_heap"; }
860 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
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
);
867 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
869 auto_diagnostic_group d
;
870 diagnostic_metadata m
;
871 m
.add_cwe (590); /* CWE-590: Free of Memory not on the Heap. */
877 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_free_of_non_heap
,
878 "%<%s%> of %qE which points to memory"
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
);
891 label_text
describe_state_change (const evdesc::state_change
&change
)
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
)
898 gimple
*def_stmt
= SSA_NAME_DEF_STMT (change
.m_expr
);
899 if (gcall
*call
= dyn_cast
<gcall
*> (def_stmt
))
901 if (is_special_named_call_p (call
, "alloca", 1)
902 || is_special_named_call_p (call
, "__builtin_alloca", 1))
904 m_kind
= KIND_ALLOCA
;
905 return label_text::borrow
906 ("memory is allocated on the stack here");
910 return label_text::borrow ("pointer is from here");
913 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
915 return ev
.formatted_print ("call to %qs here", m_funcname
);
924 const char *m_funcname
;
928 /* struct allocation_state : public state_machine::state. */
930 /* Implementation of state_machine::state::dump_to_pp vfunc
931 for allocation_state: append the API that this allocation is
935 allocation_state::dump_to_pp (pretty_printer
*pp
) const
937 state_machine::state::dump_to_pp (pp
);
939 pp_printf (pp
, " (%s)", m_api
->m_name
);
942 /* Given a allocation_state for an api, get the "nonnull" state
943 for the corresponding allocator. */
945 const allocation_state
*
946 allocation_state::get_nonnull () const
949 return as_a_allocation_state (m_api
->m_nonnull
);
952 /* malloc_state_machine's ctor. */
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
)
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
);
966 state_machine::state_t
967 malloc_state_machine::add_state (const char *name
, enum resource_state rs
,
970 return add_custom_state (new allocation_state (name
, alloc_state_id (),
974 /* Implementation of state_machine::on_stmt vfunc for malloc_state_machine. */
977 malloc_state_machine::on_stmt (sm_context
*sm_ctxt
,
978 const supernode
*node
,
979 const gimple
*stmt
) const
981 if (const gcall
*call
= dyn_cast
<const gcall
*> (stmt
))
982 if (tree callee_fndecl
= sm_ctxt
->get_fndecl_for_call (call
))
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))
993 on_allocator_call (sm_ctxt
, call
, m_malloc
);
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))
1004 on_deallocator_call (sm_ctxt
, node
, call
, m_scalar_new
);
1007 else if (is_named_call_p (callee_fndecl
, "operator delete []", call
, 1))
1009 on_deallocator_call (sm_ctxt
, node
, call
, m_vector_new
);
1013 if (is_named_call_p (callee_fndecl
, "alloca", call
, 1)
1014 || is_named_call_p (callee_fndecl
, "__builtin_alloca", call
, 1))
1016 tree lhs
= gimple_call_lhs (call
);
1018 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
, m_non_heap
);
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))
1026 on_deallocator_call (sm_ctxt
, node
, call
, m_malloc
);
1030 /* Handle "__attribute__((nonnull))". */
1032 tree fntype
= TREE_TYPE (callee_fndecl
);
1033 bitmap nonnull_args
= get_nonnull_args (fntype
);
1036 for (unsigned i
= 0; i
< gimple_call_num_args (stmt
); i
++)
1038 tree arg
= gimple_call_arg (stmt
, i
);
1039 if (TREE_CODE (TREE_TYPE (arg
)) != POINTER_TYPE
)
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
))
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
))
1051 sm_ctxt
->warn (node
, stmt
, arg
,
1052 new possible_null_arg (*this, diag_arg
,
1055 const allocation_state
*astate
1056 = as_a_allocation_state (state
);
1057 sm_ctxt
->set_next_state (stmt
, arg
,
1058 astate
->get_nonnull ());
1060 else if (state
== m_null
)
1062 sm_ctxt
->warn (node
, stmt
, arg
,
1063 new null_arg (*this, diag_arg
,
1065 sm_ctxt
->set_next_state (stmt
, arg
, m_stop
);
1069 BITMAP_FREE (nonnull_args
);
1074 if (tree lhs
= sm_ctxt
->is_zero_assignment (stmt
))
1075 if (any_pointer_p (lhs
))
1076 on_zero_assignment (sm_ctxt
, stmt
,lhs
);
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
))
1085 enum tree_code op
= gimple_assign_rhs_code (assign_stmt
);
1086 if (op
== ADDR_EXPR
)
1088 tree lhs
= gimple_assign_lhs (assign_stmt
);
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
);
1098 /* Handle dereferences. */
1099 for (unsigned i
= 0; i
< gimple_num_ops (stmt
); i
++)
1101 tree op
= gimple_op (stmt
, i
);
1104 if (TREE_CODE (op
) == COMPONENT_REF
)
1105 op
= TREE_OPERAND (op
, 0);
1107 if (TREE_CODE (op
) == MEM_REF
)
1109 tree arg
= TREE_OPERAND (op
, 0);
1110 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1112 state_t state
= sm_ctxt
->get_state (stmt
, arg
);
1113 if (unchecked_p (state
))
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 ());
1120 else if (state
== m_null
)
1122 sm_ctxt
->warn (node
, stmt
, arg
,
1123 new null_deref (*this, diag_arg
));
1124 sm_ctxt
->set_next_state (stmt
, arg
, m_stop
);
1126 else if (freed_p (state
))
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
,
1132 sm_ctxt
->set_next_state (stmt
, arg
, m_stop
);
1139 /* Handle a call to an allocator. */
1142 malloc_state_machine::on_allocator_call (sm_context
*sm_ctxt
,
1144 const api
&ap
) const
1146 tree lhs
= gimple_call_lhs (call
);
1149 if (sm_ctxt
->get_state (call
, lhs
) == m_start
)
1150 sm_ctxt
->set_next_state (call
, lhs
, ap
.m_unchecked
);
1154 /* TODO: report leak. */
1159 malloc_state_machine::on_deallocator_call (sm_context
*sm_ctxt
,
1160 const supernode
*node
,
1162 const api
&ap
) const
1164 tree arg
= gimple_call_arg (call
, 0);
1165 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1167 state_t state
= sm_ctxt
->get_state (call
, arg
);
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
))
1174 const allocation_state
*astate
= as_a_allocation_state (state
);
1176 if (astate
->m_api
!= &ap
)
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
);
1184 sm_ctxt
->set_next_state (call
, arg
, ap
.m_freed
);
1187 /* Keep state "null" as-is, rather than transitioning to "freed";
1188 we don't want to complain about double-free of NULL. */
1190 else if (state
== ap
.m_freed
)
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
);
1198 else if (state
== m_non_heap
)
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
);
1208 /* Implementation of state_machine::on_phi vfunc for malloc_state_machine. */
1211 malloc_state_machine::on_phi (sm_context
*sm_ctxt
,
1212 const supernode
*node ATTRIBUTE_UNUSED
,
1218 tree lhs
= gimple_phi_result (phi
);
1219 on_zero_assignment (sm_ctxt
, phi
, lhs
);
1223 /* Implementation of state_machine::on_condition vfunc for malloc_state_machine.
1224 Potentially transition state 'unchecked' to 'nonnull' or to 'null'. */
1227 malloc_state_machine::on_condition (sm_context
*sm_ctxt
,
1228 const supernode
*node ATTRIBUTE_UNUSED
,
1237 if (!any_pointer_p (lhs
))
1239 if (!any_pointer_p (rhs
))
1244 log ("got 'ARG != 0' match");
1245 state_t s
= sm_ctxt
->get_state (stmt
, lhs
);
1246 if (unchecked_p (s
))
1248 const allocation_state
*astate
= as_a_allocation_state (s
);
1249 sm_ctxt
->set_next_state (stmt
, lhs
, astate
->get_nonnull ());
1252 else if (op
== EQ_EXPR
)
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
);
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). */
1266 malloc_state_machine::can_purge_p (state_t s
) const
1268 enum resource_state rs
= get_rs (s
);
1269 return rs
!= RS_UNCHECKED
&& rs
!= RS_NONNULL
;
1272 /* Implementation of state_machine::on_leak vfunc for malloc_state_machine
1273 (for complaining about leaks of pointers in state 'unchecked' and
1276 pending_diagnostic
*
1277 malloc_state_machine::on_leak (tree var
) const
1279 return new malloc_leak (*this, var
);
1282 /* Implementation of state_machine::reset_when_passed_to_unknown_fn_p vfunc
1283 for malloc_state_machine. */
1286 malloc_state_machine::reset_when_passed_to_unknown_fn_p (state_t s
,
1287 bool is_mutable
) const
1289 /* An on-stack ptr doesn't stop being stack-allocated when passed to an
1291 if (s
== m_non_heap
)
1294 /* Otherwise, pointers passed as non-const can be freed. */
1298 /* Shared logic for handling GIMPLE_ASSIGNs and GIMPLE_PHIs that
1299 assign zero to LHS. */
1302 malloc_state_machine::on_zero_assignment (sm_context
*sm_ctxt
,
1306 state_t s
= sm_ctxt
->get_state (stmt
, lhs
);
1307 enum resource_state rs
= get_rs (s
);
1309 || rs
== RS_UNCHECKED
1312 sm_ctxt
->set_next_state (stmt
, lhs
, m_null
);
1315 } // anonymous namespace
1317 /* Internal interface to this file. */
1320 make_malloc_state_machine (logger
*logger
)
1322 return new malloc_state_machine (logger
);
1327 #endif /* #if ENABLE_ANALYZER */