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"
34 #include "analyzer/analyzer.h"
35 #include "diagnostic-event-id.h"
36 #include "analyzer/analyzer-logging.h"
37 #include "analyzer/sm.h"
38 #include "analyzer/pending-diagnostic.h"
41 #include "analyzer/call-string.h"
42 #include "analyzer/program-point.h"
43 #include "analyzer/store.h"
44 #include "analyzer/region-model.h"
53 class malloc_state_machine
;
55 /* An enum for discriminating between different kinds of allocation_state. */
59 /* States that are independent of api. */
61 /* The start state. */
64 /* State for a pointer that's known to be NULL. */
67 /* State for a pointer that's known to not be on the heap (e.g. to a local
71 /* Stop state, for pointers we don't want to track any more. */
74 /* States that relate to a specific api. */
76 /* State for a pointer returned from the api's allocator that hasn't
77 been checked for NULL.
78 It could be a pointer to heap-allocated memory, or could be NULL. */
81 /* State for a pointer returned from the api's allocator,
82 known to be non-NULL. */
85 /* State for a pointer passed to the api's deallocator. */
89 /* Custom state subclass, which can optionally refer to an an api. */
91 struct allocation_state
: public state_machine::state
93 allocation_state (const char *name
, unsigned id
,
94 enum resource_state rs
, const api
*a
)
95 : state (name
, id
), m_rs (rs
), m_api (a
)
98 void dump_to_pp (pretty_printer
*pp
) const FINAL OVERRIDE
;
100 const allocation_state
*get_nonnull () const;
102 enum resource_state m_rs
;
106 /* An enum for choosing which wording to use in various diagnostics
107 when describing deallocations. */
115 /* Represents a particular family of API calls for allocating/deallocating
116 heap memory that should be matched e.g.
119 - vector new[]/delete[]
122 We track the expected deallocation function, but not the allocation
123 function - there could be more than one allocator per deallocator. For
124 example, there could be dozens of allocators for "free" beyond just
125 malloc e.g. calloc, xstrdup, etc. We don't want to explode the number
126 of states by tracking individual allocators in the exploded graph;
127 we merely want to track "this value expects to have 'free' called on it".
128 Perhaps we can reconstruct which allocator was used later, when emitting
129 the path, if it's necessary for precision of wording of diagnostics. */
133 api (malloc_state_machine
*sm
,
135 const char *dealloc_funcname
,
136 enum wording wording
);
138 /* An internal name for identifying this API in dumps. */
141 /* The name of the deallocation function, for use in diagnostics. */
142 const char *m_dealloc_funcname
;
144 /* Which wording to use in diagnostics. */
145 enum wording m_wording
;
147 /* Pointers to api-specific states.
148 These states are owned by the state_machine base class. */
150 /* State for an unchecked result from this api's allocator. */
151 state_machine::state_t m_unchecked
;
153 /* State for a known non-NULL result from this apis's allocator. */
154 state_machine::state_t m_nonnull
;
156 /* State for a value passed to this api's deallocator. */
157 state_machine::state_t m_freed
;
160 /* A state machine for detecting misuses of the malloc/free API.
162 See sm-malloc.dot for an overview (keep this in-sync with that file). */
164 class malloc_state_machine
: public state_machine
167 typedef allocation_state custom_data_t
;
169 malloc_state_machine (logger
*logger
);
172 add_state (const char *name
, enum resource_state rs
, const api
*a
);
174 bool inherited_state_p () const FINAL OVERRIDE
{ return false; }
176 state_machine::state_t
177 get_default_state (const svalue
*sval
) const FINAL OVERRIDE
179 if (tree cst
= sval
->maybe_get_constant ())
184 if (const region_svalue
*ptr
= sval
->dyn_cast_region_svalue ())
186 const region
*reg
= ptr
->get_pointee ();
187 const region
*base_reg
= reg
->get_base_region ();
188 if (base_reg
->get_kind () == RK_DECL
189 || base_reg
->get_kind () == RK_STRING
)
195 bool on_stmt (sm_context
*sm_ctxt
,
196 const supernode
*node
,
197 const gimple
*stmt
) const FINAL OVERRIDE
;
199 void on_phi (sm_context
*sm_ctxt
,
200 const supernode
*node
,
202 tree rhs
) const FINAL OVERRIDE
;
204 void on_condition (sm_context
*sm_ctxt
,
205 const supernode
*node
,
209 tree rhs
) const FINAL OVERRIDE
;
211 bool can_purge_p (state_t s
) const FINAL OVERRIDE
;
212 pending_diagnostic
*on_leak (tree var
) const FINAL OVERRIDE
;
214 bool reset_when_passed_to_unknown_fn_p (state_t s
,
215 bool is_mutable
) const FINAL OVERRIDE
;
221 /* States that are independent of api. */
223 /* State for a pointer that's known to be NULL. */
226 /* State for a pointer that's known to not be on the heap (e.g. to a local
228 state_t m_non_heap
; // TODO: or should this be a different state machine?
229 // or do we need child values etc?
231 /* Stop state, for pointers we don't want to track any more. */
235 void on_allocator_call (sm_context
*sm_ctxt
,
237 const api
&ap
) const;
238 void on_deallocator_call (sm_context
*sm_ctxt
,
239 const supernode
*node
,
241 const api
&ap
) const;
242 void on_zero_assignment (sm_context
*sm_ctxt
,
249 api::api (malloc_state_machine
*sm
,
251 const char *dealloc_funcname
,
252 enum wording wording
)
254 m_dealloc_funcname (dealloc_funcname
),
256 m_unchecked (sm
->add_state ("unchecked", RS_UNCHECKED
, this)),
257 m_nonnull (sm
->add_state ("nonnull", RS_NONNULL
, this)),
258 m_freed (sm
->add_state ("freed", RS_FREED
, this))
262 /* Return STATE cast to the custom state subclass, or NULL for the start state.
263 Everything should be an allocation_state apart from the start state. */
265 static const allocation_state
*
266 dyn_cast_allocation_state (state_machine::state_t state
)
268 if (state
->get_id () == 0)
270 return static_cast <const allocation_state
*> (state
);
273 /* Return STATE cast to the custom state subclass, for a state that is
274 already known to not be the start state . */
276 static const allocation_state
*
277 as_a_allocation_state (state_machine::state_t state
)
279 gcc_assert (state
->get_id () != 0);
280 return static_cast <const allocation_state
*> (state
);
283 /* Get the resource_state for STATE. */
285 static enum resource_state
286 get_rs (state_machine::state_t state
)
288 if (const allocation_state
*astate
= dyn_cast_allocation_state (state
))
294 /* Return true if STATE is an unchecked result from an allocator. */
297 unchecked_p (state_machine::state_t state
)
299 return get_rs (state
) == RS_UNCHECKED
;
302 /* Return true if STATE is a non-null result from an allocator. */
305 nonnull_p (state_machine::state_t state
)
307 return get_rs (state
) == RS_NONNULL
;
310 /* Return true if STATE is a value that has been passed to a deallocator. */
313 freed_p (state_machine::state_t state
)
315 return get_rs (state
) == RS_FREED
;
318 /* Class for diagnostics relating to malloc_state_machine. */
320 class malloc_diagnostic
: public pending_diagnostic
323 malloc_diagnostic (const malloc_state_machine
&sm
, tree arg
)
324 : m_sm (sm
), m_arg (arg
)
327 bool subclass_equal_p (const pending_diagnostic
&base_other
) const OVERRIDE
329 return same_tree_p (m_arg
, ((const malloc_diagnostic
&)base_other
).m_arg
);
332 label_text
describe_state_change (const evdesc::state_change
&change
)
335 if (change
.m_old_state
== m_sm
.get_start_state ()
336 && unchecked_p (change
.m_new_state
))
337 // TODO: verify that it's the allocation stmt, not a copy
338 return label_text::borrow ("allocated here");
339 if (unchecked_p (change
.m_old_state
)
340 && nonnull_p (change
.m_new_state
))
343 return change
.formatted_print ("assuming %qE is non-NULL",
346 return change
.formatted_print ("assuming %qs is non-NULL",
349 if (change
.m_new_state
== m_sm
.m_null
)
351 if (unchecked_p (change
.m_old_state
))
354 return change
.formatted_print ("assuming %qE is NULL",
357 return change
.formatted_print ("assuming %qs is NULL",
363 return change
.formatted_print ("%qE is NULL",
366 return change
.formatted_print ("%qs is NULL",
371 return label_text ();
375 const malloc_state_machine
&m_sm
;
379 /* Concrete subclass for reporting mismatching allocator/deallocator
382 class mismatching_deallocation
: public malloc_diagnostic
385 mismatching_deallocation (const malloc_state_machine
&sm
, tree arg
,
386 const api
*expected_dealloc
,
387 const api
*actual_dealloc
)
388 : malloc_diagnostic (sm
, arg
),
389 m_expected_dealloc (expected_dealloc
),
390 m_actual_dealloc (actual_dealloc
)
393 const char *get_kind () const FINAL OVERRIDE
395 return "mismatching_deallocation";
398 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
400 auto_diagnostic_group d
;
401 diagnostic_metadata m
;
402 m
.add_cwe (762); /* CWE-762: Mismatched Memory Management Routines. */
403 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_mismatching_deallocation
,
404 "%qE should have been deallocated with %qs"
405 " but was deallocated with %qs",
406 m_arg
, m_expected_dealloc
->m_dealloc_funcname
,
407 m_actual_dealloc
->m_dealloc_funcname
);
410 label_text
describe_state_change (const evdesc::state_change
&change
)
413 if (unchecked_p (change
.m_new_state
))
415 m_alloc_event
= change
.m_event_id
;
416 return change
.formatted_print ("allocated here"
417 " (expects deallocation with %qs)",
418 m_expected_dealloc
->m_dealloc_funcname
);
420 return malloc_diagnostic::describe_state_change (change
);
423 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
425 if (m_alloc_event
.known_p ())
426 return ev
.formatted_print
427 ("deallocated with %qs here;"
428 " allocation at %@ expects deallocation with %qs",
429 m_actual_dealloc
->m_dealloc_funcname
, &m_alloc_event
,
430 m_expected_dealloc
->m_dealloc_funcname
);
431 return ev
.formatted_print ("deallocated with %qs here",
432 m_actual_dealloc
->m_dealloc_funcname
);
436 diagnostic_event_id_t m_alloc_event
;
437 const api
*m_expected_dealloc
;
438 const api
*m_actual_dealloc
;
441 /* Concrete subclass for reporting double-free diagnostics. */
443 class double_free
: public malloc_diagnostic
446 double_free (const malloc_state_machine
&sm
, tree arg
, const char *funcname
)
447 : malloc_diagnostic (sm
, arg
), m_funcname (funcname
)
450 const char *get_kind () const FINAL OVERRIDE
{ return "double_free"; }
452 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
454 auto_diagnostic_group d
;
455 diagnostic_metadata m
;
456 m
.add_cwe (415); /* CWE-415: Double Free. */
457 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_double_free
,
458 "double-%<%s%> of %qE", m_funcname
, m_arg
);
461 label_text
describe_state_change (const evdesc::state_change
&change
)
464 if (freed_p (change
.m_new_state
))
466 m_first_free_event
= change
.m_event_id
;
467 return change
.formatted_print ("first %qs here", m_funcname
);
469 return malloc_diagnostic::describe_state_change (change
);
472 label_text
describe_call_with_state (const evdesc::call_with_state
&info
)
475 if (freed_p (info
.m_state
))
476 return info
.formatted_print
477 ("passing freed pointer %qE in call to %qE from %qE",
478 info
.m_expr
, info
.m_callee_fndecl
, info
.m_caller_fndecl
);
479 return label_text ();
482 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
484 if (m_first_free_event
.known_p ())
485 return ev
.formatted_print ("second %qs here; first %qs was at %@",
486 m_funcname
, m_funcname
,
487 &m_first_free_event
);
488 return ev
.formatted_print ("second %qs here", m_funcname
);
492 diagnostic_event_id_t m_first_free_event
;
493 const char *m_funcname
;
496 /* Abstract subclass for describing possible bad uses of NULL.
497 Responsible for describing the call that could return NULL. */
499 class possible_null
: public malloc_diagnostic
502 possible_null (const malloc_state_machine
&sm
, tree arg
)
503 : malloc_diagnostic (sm
, arg
)
506 label_text
describe_state_change (const evdesc::state_change
&change
)
509 if (change
.m_old_state
== m_sm
.get_start_state ()
510 && unchecked_p (change
.m_new_state
))
512 m_origin_of_unchecked_event
= change
.m_event_id
;
513 return label_text::borrow ("this call could return NULL");
515 return malloc_diagnostic::describe_state_change (change
);
518 label_text
describe_return_of_state (const evdesc::return_of_state
&info
)
521 if (unchecked_p (info
.m_state
))
522 return info
.formatted_print ("possible return of NULL to %qE from %qE",
523 info
.m_caller_fndecl
, info
.m_callee_fndecl
);
524 return label_text ();
528 diagnostic_event_id_t m_origin_of_unchecked_event
;
531 /* Concrete subclass for describing dereference of a possible NULL
534 class possible_null_deref
: public possible_null
537 possible_null_deref (const malloc_state_machine
&sm
, tree arg
)
538 : possible_null (sm
, arg
)
541 const char *get_kind () const FINAL OVERRIDE
{ return "possible_null_deref"; }
543 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
545 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
546 diagnostic_metadata m
;
548 return warning_meta (rich_loc
, m
,
549 OPT_Wanalyzer_possible_null_dereference
,
550 "dereference of possibly-NULL %qE", m_arg
);
553 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
555 if (m_origin_of_unchecked_event
.known_p ())
556 return ev
.formatted_print ("%qE could be NULL: unchecked value from %@",
558 &m_origin_of_unchecked_event
);
560 return ev
.formatted_print ("%qE could be NULL", ev
.m_expr
);
565 /* Return true if FNDECL is a C++ method. */
568 method_p (tree fndecl
)
570 return TREE_CODE (TREE_TYPE (fndecl
)) == METHOD_TYPE
;
573 /* Return a 1-based description of ARG_IDX (0-based) of FNDECL.
574 Compare with %P in the C++ FE (implemented in cp/error.c: parm_to_string
575 as called from cp_printer). */
578 describe_argument_index (tree fndecl
, int arg_idx
)
580 if (method_p (fndecl
))
582 return label_text::borrow ("'this'");
584 pp_printf (&pp
, "%u", arg_idx
+ 1 - method_p (fndecl
));
585 return label_text::take (xstrdup (pp_formatted_text (&pp
)));
588 /* Subroutine for use by possible_null_arg::emit and null_arg::emit.
589 Issue a note informing that the pertinent argument must be non-NULL. */
592 inform_nonnull_attribute (tree fndecl
, int arg_idx
)
594 label_text arg_desc
= describe_argument_index (fndecl
, arg_idx
);
595 inform (DECL_SOURCE_LOCATION (fndecl
),
596 "argument %s of %qD must be non-null",
597 arg_desc
.m_buffer
, fndecl
);
598 arg_desc
.maybe_free ();
599 /* Ideally we would use the location of the parm and underline the
600 attribute also - but we don't have the location_t values at this point
602 For reference, the C and C++ FEs have get_fndecl_argument_location. */
605 /* Concrete subclass for describing passing a possibly-NULL value to a
606 function marked with __attribute__((nonnull)). */
608 class possible_null_arg
: public possible_null
611 possible_null_arg (const malloc_state_machine
&sm
, tree arg
,
612 tree fndecl
, int arg_idx
)
613 : possible_null (sm
, arg
),
614 m_fndecl (fndecl
), m_arg_idx (arg_idx
)
617 const char *get_kind () const FINAL OVERRIDE
{ return "possible_null_arg"; }
619 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
621 const possible_null_arg
&sub_other
622 = (const possible_null_arg
&)base_other
;
623 return (same_tree_p (m_arg
, sub_other
.m_arg
)
624 && m_fndecl
== sub_other
.m_fndecl
625 && m_arg_idx
== sub_other
.m_arg_idx
);
629 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
631 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
632 auto_diagnostic_group d
;
633 diagnostic_metadata m
;
636 = warning_meta (rich_loc
, m
, OPT_Wanalyzer_possible_null_argument
,
637 "use of possibly-NULL %qE where non-null expected",
640 inform_nonnull_attribute (m_fndecl
, m_arg_idx
);
644 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
646 label_text arg_desc
= describe_argument_index (m_fndecl
, m_arg_idx
);
648 if (m_origin_of_unchecked_event
.known_p ())
649 result
= ev
.formatted_print ("argument %s (%qE) from %@ could be NULL"
650 " where non-null expected",
651 arg_desc
.m_buffer
, ev
.m_expr
,
652 &m_origin_of_unchecked_event
);
654 result
= ev
.formatted_print ("argument %s (%qE) could be NULL"
655 " where non-null expected",
656 arg_desc
.m_buffer
, ev
.m_expr
);
657 arg_desc
.maybe_free ();
666 /* Concrete subclass for describing a dereference of a NULL value. */
668 class null_deref
: public malloc_diagnostic
671 null_deref (const malloc_state_machine
&sm
, tree arg
)
672 : malloc_diagnostic (sm
, arg
) {}
674 const char *get_kind () const FINAL OVERRIDE
{ return "null_deref"; }
676 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
678 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
679 diagnostic_metadata m
;
681 return warning_meta (rich_loc
, m
,
682 OPT_Wanalyzer_null_dereference
,
683 "dereference of NULL %qE", m_arg
);
686 label_text
describe_return_of_state (const evdesc::return_of_state
&info
)
689 if (info
.m_state
== m_sm
.m_null
)
690 return info
.formatted_print ("return of NULL to %qE from %qE",
691 info
.m_caller_fndecl
, info
.m_callee_fndecl
);
692 return label_text ();
695 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
697 return ev
.formatted_print ("dereference of NULL %qE", ev
.m_expr
);
701 /* Concrete subclass for describing passing a NULL value to a
702 function marked with __attribute__((nonnull)). */
704 class null_arg
: public malloc_diagnostic
707 null_arg (const malloc_state_machine
&sm
, tree arg
,
708 tree fndecl
, int arg_idx
)
709 : malloc_diagnostic (sm
, arg
),
710 m_fndecl (fndecl
), m_arg_idx (arg_idx
)
713 const char *get_kind () const FINAL OVERRIDE
{ return "null_arg"; }
715 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
717 const null_arg
&sub_other
718 = (const null_arg
&)base_other
;
719 return (same_tree_p (m_arg
, sub_other
.m_arg
)
720 && m_fndecl
== sub_other
.m_fndecl
721 && m_arg_idx
== sub_other
.m_arg_idx
);
724 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
726 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
727 auto_diagnostic_group d
;
728 diagnostic_metadata m
;
733 warned
= warning_meta (rich_loc
, m
, OPT_Wanalyzer_null_argument
,
734 "use of NULL where non-null expected");
736 warned
= warning_meta (rich_loc
, m
, OPT_Wanalyzer_null_argument
,
737 "use of NULL %qE where non-null expected",
740 inform_nonnull_attribute (m_fndecl
, m_arg_idx
);
744 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
746 label_text arg_desc
= describe_argument_index (m_fndecl
, m_arg_idx
);
748 if (zerop (ev
.m_expr
))
749 result
= ev
.formatted_print ("argument %s NULL where non-null expected",
752 result
= ev
.formatted_print ("argument %s (%qE) NULL"
753 " where non-null expected",
754 arg_desc
.m_buffer
, ev
.m_expr
);
755 arg_desc
.maybe_free ();
764 class use_after_free
: public malloc_diagnostic
767 use_after_free (const malloc_state_machine
&sm
, tree arg
,
769 : malloc_diagnostic (sm
, arg
), m_api (a
)
772 const char *get_kind () const FINAL OVERRIDE
{ return "use_after_free"; }
774 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
776 /* CWE-416: Use After Free. */
777 diagnostic_metadata m
;
779 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_use_after_free
,
780 "use after %<%s%> of %qE",
781 m_api
->m_dealloc_funcname
, m_arg
);
784 label_text
describe_state_change (const evdesc::state_change
&change
)
787 if (freed_p (change
.m_new_state
))
789 m_free_event
= change
.m_event_id
;
790 switch (m_api
->m_wording
)
795 return label_text::borrow ("freed here");
796 case WORDING_DELETED
:
797 return label_text::borrow ("deleted here");
800 return malloc_diagnostic::describe_state_change (change
);
803 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
805 const char *funcname
= m_api
->m_dealloc_funcname
;
806 if (m_free_event
.known_p ())
807 switch (m_api
->m_wording
)
812 return ev
.formatted_print ("use after %<%s%> of %qE; freed at %@",
813 funcname
, ev
.m_expr
, &m_free_event
);
814 case WORDING_DELETED
:
815 return ev
.formatted_print ("use after %<%s%> of %qE; deleted at %@",
816 funcname
, ev
.m_expr
, &m_free_event
);
819 return ev
.formatted_print ("use after %<%s%> of %qE",
820 funcname
, ev
.m_expr
);
824 diagnostic_event_id_t m_free_event
;
828 class malloc_leak
: public malloc_diagnostic
831 malloc_leak (const malloc_state_machine
&sm
, tree arg
)
832 : malloc_diagnostic (sm
, arg
) {}
834 const char *get_kind () const FINAL OVERRIDE
{ return "malloc_leak"; }
836 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
838 diagnostic_metadata m
;
841 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_malloc_leak
,
842 "leak of %qE", m_arg
);
844 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_malloc_leak
,
845 "leak of %qs", "<unknown>");
848 label_text
describe_state_change (const evdesc::state_change
&change
)
851 if (unchecked_p (change
.m_new_state
))
853 m_alloc_event
= change
.m_event_id
;
854 return label_text::borrow ("allocated here");
856 return malloc_diagnostic::describe_state_change (change
);
859 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
863 if (m_alloc_event
.known_p ())
864 return ev
.formatted_print ("%qE leaks here; was allocated at %@",
865 ev
.m_expr
, &m_alloc_event
);
867 return ev
.formatted_print ("%qE leaks here", ev
.m_expr
);
871 if (m_alloc_event
.known_p ())
872 return ev
.formatted_print ("%qs leaks here; was allocated at %@",
873 "<unknown>", &m_alloc_event
);
875 return ev
.formatted_print ("%qs leaks here", "<unknown>");
880 diagnostic_event_id_t m_alloc_event
;
883 class free_of_non_heap
: public malloc_diagnostic
886 free_of_non_heap (const malloc_state_machine
&sm
, tree arg
,
887 const char *funcname
)
888 : malloc_diagnostic (sm
, arg
), m_funcname (funcname
), m_kind (KIND_UNKNOWN
)
892 const char *get_kind () const FINAL OVERRIDE
{ return "free_of_non_heap"; }
894 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
897 const free_of_non_heap
&other
= (const free_of_non_heap
&)base_other
;
898 return (same_tree_p (m_arg
, other
.m_arg
) && m_kind
== other
.m_kind
);
901 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
903 auto_diagnostic_group d
;
904 diagnostic_metadata m
;
905 m
.add_cwe (590); /* CWE-590: Free of Memory not on the Heap. */
911 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_free_of_non_heap
,
912 "%<%s%> of %qE which points to memory"
917 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_free_of_non_heap
,
918 "%<%s%> of memory allocated on the stack by"
919 " %qs (%qE) will corrupt the heap",
920 m_funcname
, "alloca", m_arg
);
925 label_text
describe_state_change (const evdesc::state_change
&change
)
928 /* Attempt to reconstruct what kind of pointer it is.
929 (It seems neater for this to be a part of the state, though). */
930 if (TREE_CODE (change
.m_expr
) == SSA_NAME
)
932 gimple
*def_stmt
= SSA_NAME_DEF_STMT (change
.m_expr
);
933 if (gcall
*call
= dyn_cast
<gcall
*> (def_stmt
))
935 if (is_special_named_call_p (call
, "alloca", 1)
936 || is_special_named_call_p (call
, "__builtin_alloca", 1))
938 m_kind
= KIND_ALLOCA
;
939 return label_text::borrow
940 ("memory is allocated on the stack here");
944 return label_text::borrow ("pointer is from here");
947 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
949 return ev
.formatted_print ("call to %qs here", m_funcname
);
958 const char *m_funcname
;
962 /* struct allocation_state : public state_machine::state. */
964 /* Implementation of state_machine::state::dump_to_pp vfunc
965 for allocation_state: append the API that this allocation is
969 allocation_state::dump_to_pp (pretty_printer
*pp
) const
971 state_machine::state::dump_to_pp (pp
);
973 pp_printf (pp
, " (%s)", m_api
->m_name
);
976 /* Given a allocation_state for an api, get the "nonnull" state
977 for the corresponding allocator. */
979 const allocation_state
*
980 allocation_state::get_nonnull () const
983 return as_a_allocation_state (m_api
->m_nonnull
);
986 /* malloc_state_machine's ctor. */
988 malloc_state_machine::malloc_state_machine (logger
*logger
)
989 : state_machine ("malloc", logger
),
990 m_malloc (this, "malloc", "free", WORDING_FREED
),
991 m_scalar_new (this, "new", "delete", WORDING_DELETED
),
992 m_vector_new (this, "new[]", "delete[]", WORDING_DELETED
)
994 gcc_assert (m_start
->get_id () == 0);
995 m_null
= add_state ("null", RS_FREED
, NULL
);
996 m_non_heap
= add_state ("non-heap", RS_NON_HEAP
, NULL
);
997 m_stop
= add_state ("stop", RS_STOP
, NULL
);
1000 state_machine::state_t
1001 malloc_state_machine::add_state (const char *name
, enum resource_state rs
,
1004 return add_custom_state (new allocation_state (name
, alloc_state_id (),
1008 /* Implementation of state_machine::on_stmt vfunc for malloc_state_machine. */
1011 malloc_state_machine::on_stmt (sm_context
*sm_ctxt
,
1012 const supernode
*node
,
1013 const gimple
*stmt
) const
1015 if (const gcall
*call
= dyn_cast
<const gcall
*> (stmt
))
1016 if (tree callee_fndecl
= sm_ctxt
->get_fndecl_for_call (call
))
1018 if (is_named_call_p (callee_fndecl
, "malloc", call
, 1)
1019 || is_named_call_p (callee_fndecl
, "calloc", call
, 2)
1020 || is_std_named_call_p (callee_fndecl
, "malloc", call
, 1)
1021 || is_std_named_call_p (callee_fndecl
, "calloc", call
, 2)
1022 || is_named_call_p (callee_fndecl
, "__builtin_malloc", call
, 1)
1023 || is_named_call_p (callee_fndecl
, "__builtin_calloc", call
, 2)
1024 || is_named_call_p (callee_fndecl
, "strdup", call
, 1)
1025 || is_named_call_p (callee_fndecl
, "strndup", call
, 2))
1027 on_allocator_call (sm_ctxt
, call
, m_malloc
);
1031 if (is_named_call_p (callee_fndecl
, "operator new", call
, 1))
1032 on_allocator_call (sm_ctxt
, call
, m_scalar_new
);
1033 else if (is_named_call_p (callee_fndecl
, "operator new []", call
, 1))
1034 on_allocator_call (sm_ctxt
, call
, m_vector_new
);
1035 else if (is_named_call_p (callee_fndecl
, "operator delete", call
, 1)
1036 || is_named_call_p (callee_fndecl
, "operator delete", call
, 2))
1038 on_deallocator_call (sm_ctxt
, node
, call
, m_scalar_new
);
1041 else if (is_named_call_p (callee_fndecl
, "operator delete []", call
, 1))
1043 on_deallocator_call (sm_ctxt
, node
, call
, m_vector_new
);
1047 if (is_named_call_p (callee_fndecl
, "alloca", call
, 1)
1048 || is_named_call_p (callee_fndecl
, "__builtin_alloca", call
, 1))
1050 tree lhs
= gimple_call_lhs (call
);
1052 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
, m_non_heap
);
1056 if (is_named_call_p (callee_fndecl
, "free", call
, 1)
1057 || is_std_named_call_p (callee_fndecl
, "free", call
, 1)
1058 || is_named_call_p (callee_fndecl
, "__builtin_free", call
, 1))
1060 on_deallocator_call (sm_ctxt
, node
, call
, m_malloc
);
1064 /* Handle "__attribute__((nonnull))". */
1066 tree fntype
= TREE_TYPE (callee_fndecl
);
1067 bitmap nonnull_args
= get_nonnull_args (fntype
);
1070 for (unsigned i
= 0; i
< gimple_call_num_args (stmt
); i
++)
1072 tree arg
= gimple_call_arg (stmt
, i
);
1073 if (TREE_CODE (TREE_TYPE (arg
)) != POINTER_TYPE
)
1075 /* If we have a nonnull-args, and either all pointers, or just
1076 the specified pointers. */
1077 if (bitmap_empty_p (nonnull_args
)
1078 || bitmap_bit_p (nonnull_args
, i
))
1080 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1081 state_t state
= sm_ctxt
->get_state (stmt
, arg
);
1082 /* Can't use a switch as the states are non-const. */
1083 if (unchecked_p (state
))
1085 sm_ctxt
->warn (node
, stmt
, arg
,
1086 new possible_null_arg (*this, diag_arg
,
1089 const allocation_state
*astate
1090 = as_a_allocation_state (state
);
1091 sm_ctxt
->set_next_state (stmt
, arg
,
1092 astate
->get_nonnull ());
1094 else if (state
== m_null
)
1096 sm_ctxt
->warn (node
, stmt
, arg
,
1097 new null_arg (*this, diag_arg
,
1099 sm_ctxt
->set_next_state (stmt
, arg
, m_stop
);
1103 BITMAP_FREE (nonnull_args
);
1108 if (tree lhs
= sm_ctxt
->is_zero_assignment (stmt
))
1109 if (any_pointer_p (lhs
))
1110 on_zero_assignment (sm_ctxt
, stmt
,lhs
);
1112 /* If we have "LHS = &EXPR;" and EXPR is something other than a MEM_REF,
1113 transition LHS from start to non_heap.
1114 Doing it for ADDR_EXPR(MEM_REF()) is likely wrong, and can lead to
1115 unbounded chains of unmergeable sm-state on pointer arithmetic in loops
1116 when optimization is enabled. */
1117 if (const gassign
*assign_stmt
= dyn_cast
<const gassign
*> (stmt
))
1119 enum tree_code op
= gimple_assign_rhs_code (assign_stmt
);
1120 if (op
== ADDR_EXPR
)
1122 tree lhs
= gimple_assign_lhs (assign_stmt
);
1125 tree addr_expr
= gimple_assign_rhs1 (assign_stmt
);
1126 if (TREE_CODE (TREE_OPERAND (addr_expr
, 0)) != MEM_REF
)
1127 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
, m_non_heap
);
1132 /* Handle dereferences. */
1133 for (unsigned i
= 0; i
< gimple_num_ops (stmt
); i
++)
1135 tree op
= gimple_op (stmt
, i
);
1138 if (TREE_CODE (op
) == COMPONENT_REF
)
1139 op
= TREE_OPERAND (op
, 0);
1141 if (TREE_CODE (op
) == MEM_REF
)
1143 tree arg
= TREE_OPERAND (op
, 0);
1144 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1146 state_t state
= sm_ctxt
->get_state (stmt
, arg
);
1147 if (unchecked_p (state
))
1149 sm_ctxt
->warn (node
, stmt
, arg
,
1150 new possible_null_deref (*this, diag_arg
));
1151 const allocation_state
*astate
= as_a_allocation_state (state
);
1152 sm_ctxt
->set_next_state (stmt
, arg
, astate
->get_nonnull ());
1154 else if (state
== m_null
)
1156 sm_ctxt
->warn (node
, stmt
, arg
,
1157 new null_deref (*this, diag_arg
));
1158 sm_ctxt
->set_next_state (stmt
, arg
, m_stop
);
1160 else if (freed_p (state
))
1162 const allocation_state
*astate
= as_a_allocation_state (state
);
1163 sm_ctxt
->warn (node
, stmt
, arg
,
1164 new use_after_free (*this, diag_arg
,
1166 sm_ctxt
->set_next_state (stmt
, arg
, m_stop
);
1173 /* Handle a call to an allocator. */
1176 malloc_state_machine::on_allocator_call (sm_context
*sm_ctxt
,
1178 const api
&ap
) const
1180 tree lhs
= gimple_call_lhs (call
);
1183 if (sm_ctxt
->get_state (call
, lhs
) == m_start
)
1184 sm_ctxt
->set_next_state (call
, lhs
, ap
.m_unchecked
);
1188 /* TODO: report leak. */
1193 malloc_state_machine::on_deallocator_call (sm_context
*sm_ctxt
,
1194 const supernode
*node
,
1196 const api
&ap
) const
1198 tree arg
= gimple_call_arg (call
, 0);
1199 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1201 state_t state
= sm_ctxt
->get_state (call
, arg
);
1203 /* start/unchecked/nonnull -> freed. */
1204 if (state
== m_start
)
1205 sm_ctxt
->set_next_state (call
, arg
, ap
.m_freed
);
1206 else if (unchecked_p (state
) || nonnull_p (state
))
1208 const allocation_state
*astate
= as_a_allocation_state (state
);
1210 if (astate
->m_api
!= &ap
)
1212 /* Wrong allocator. */
1213 pending_diagnostic
*d
1214 = new mismatching_deallocation (*this, diag_arg
,
1215 astate
->m_api
, &ap
);
1216 sm_ctxt
->warn (node
, call
, arg
, d
);
1218 sm_ctxt
->set_next_state (call
, arg
, ap
.m_freed
);
1221 /* Keep state "null" as-is, rather than transitioning to "freed";
1222 we don't want to complain about double-free of NULL. */
1224 else if (state
== ap
.m_freed
)
1226 /* freed -> stop, with warning. */
1227 sm_ctxt
->warn (node
, call
, arg
,
1228 new double_free (*this, diag_arg
,
1229 ap
.m_dealloc_funcname
));
1230 sm_ctxt
->set_next_state (call
, arg
, m_stop
);
1232 else if (state
== m_non_heap
)
1234 /* non-heap -> stop, with warning. */
1235 sm_ctxt
->warn (node
, call
, arg
,
1236 new free_of_non_heap (*this, diag_arg
,
1237 ap
.m_dealloc_funcname
));
1238 sm_ctxt
->set_next_state (call
, arg
, m_stop
);
1242 /* Implementation of state_machine::on_phi vfunc for malloc_state_machine. */
1245 malloc_state_machine::on_phi (sm_context
*sm_ctxt
,
1246 const supernode
*node ATTRIBUTE_UNUSED
,
1252 tree lhs
= gimple_phi_result (phi
);
1253 on_zero_assignment (sm_ctxt
, phi
, lhs
);
1257 /* Implementation of state_machine::on_condition vfunc for malloc_state_machine.
1258 Potentially transition state 'unchecked' to 'nonnull' or to 'null'. */
1261 malloc_state_machine::on_condition (sm_context
*sm_ctxt
,
1262 const supernode
*node ATTRIBUTE_UNUSED
,
1271 if (!any_pointer_p (lhs
))
1273 if (!any_pointer_p (rhs
))
1278 log ("got 'ARG != 0' match");
1279 state_t s
= sm_ctxt
->get_state (stmt
, lhs
);
1280 if (unchecked_p (s
))
1282 const allocation_state
*astate
= as_a_allocation_state (s
);
1283 sm_ctxt
->set_next_state (stmt
, lhs
, astate
->get_nonnull ());
1286 else if (op
== EQ_EXPR
)
1288 log ("got 'ARG == 0' match");
1289 state_t s
= sm_ctxt
->get_state (stmt
, lhs
);
1290 if (unchecked_p (s
))
1291 sm_ctxt
->set_next_state (stmt
, lhs
, m_null
);
1295 /* Implementation of state_machine::can_purge_p vfunc for malloc_state_machine.
1296 Don't allow purging of pointers in state 'unchecked' or 'nonnull'
1297 (to avoid false leak reports). */
1300 malloc_state_machine::can_purge_p (state_t s
) const
1302 enum resource_state rs
= get_rs (s
);
1303 return rs
!= RS_UNCHECKED
&& rs
!= RS_NONNULL
;
1306 /* Implementation of state_machine::on_leak vfunc for malloc_state_machine
1307 (for complaining about leaks of pointers in state 'unchecked' and
1310 pending_diagnostic
*
1311 malloc_state_machine::on_leak (tree var
) const
1313 return new malloc_leak (*this, var
);
1316 /* Implementation of state_machine::reset_when_passed_to_unknown_fn_p vfunc
1317 for malloc_state_machine. */
1320 malloc_state_machine::reset_when_passed_to_unknown_fn_p (state_t s
,
1321 bool is_mutable
) const
1323 /* An on-stack ptr doesn't stop being stack-allocated when passed to an
1325 if (s
== m_non_heap
)
1328 /* Otherwise, pointers passed as non-const can be freed. */
1332 /* Shared logic for handling GIMPLE_ASSIGNs and GIMPLE_PHIs that
1333 assign zero to LHS. */
1336 malloc_state_machine::on_zero_assignment (sm_context
*sm_ctxt
,
1340 state_t s
= sm_ctxt
->get_state (stmt
, lhs
);
1341 enum resource_state rs
= get_rs (s
);
1343 || rs
== RS_UNCHECKED
1346 sm_ctxt
->set_next_state (stmt
, lhs
, m_null
);
1349 } // anonymous namespace
1351 /* Internal interface to this file. */
1354 make_malloc_state_machine (logger
*logger
)
1356 return new malloc_state_machine (logger
);
1361 #endif /* #if ENABLE_ANALYZER */