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 /* Subroutine for use by possible_null_arg::emit and null_arg::emit.
566 Issue a note informing that the pertinent argument must be non-NULL. */
569 inform_nonnull_attribute (tree fndecl
, int arg_idx
)
571 inform (DECL_SOURCE_LOCATION (fndecl
),
572 "argument %u of %qD must be non-null",
573 arg_idx
+ 1, fndecl
);
574 /* Ideally we would use the location of the parm and underline the
575 attribute also - but we don't have the location_t values at this point
577 For reference, the C and C++ FEs have get_fndecl_argument_location. */
580 /* Concrete subclass for describing passing a possibly-NULL value to a
581 function marked with __attribute__((nonnull)). */
583 class possible_null_arg
: public possible_null
586 possible_null_arg (const malloc_state_machine
&sm
, tree arg
,
587 tree fndecl
, int arg_idx
)
588 : possible_null (sm
, arg
),
589 m_fndecl (fndecl
), m_arg_idx (arg_idx
)
592 const char *get_kind () const FINAL OVERRIDE
{ return "possible_null_arg"; }
594 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
596 const possible_null_arg
&sub_other
597 = (const possible_null_arg
&)base_other
;
598 return (same_tree_p (m_arg
, sub_other
.m_arg
)
599 && m_fndecl
== sub_other
.m_fndecl
600 && m_arg_idx
== sub_other
.m_arg_idx
);
604 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
606 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
607 auto_diagnostic_group d
;
608 diagnostic_metadata m
;
611 = warning_meta (rich_loc
, m
, OPT_Wanalyzer_possible_null_argument
,
612 "use of possibly-NULL %qE where non-null expected",
615 inform_nonnull_attribute (m_fndecl
, m_arg_idx
);
619 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
621 if (m_origin_of_unchecked_event
.known_p ())
622 return ev
.formatted_print ("argument %u (%qE) from %@ could be NULL"
623 " where non-null expected",
624 m_arg_idx
+ 1, ev
.m_expr
,
625 &m_origin_of_unchecked_event
);
627 return ev
.formatted_print ("argument %u (%qE) could be NULL"
628 " where non-null expected",
629 m_arg_idx
+ 1, ev
.m_expr
);
637 /* Concrete subclass for describing a dereference of a NULL value. */
639 class null_deref
: public malloc_diagnostic
642 null_deref (const malloc_state_machine
&sm
, tree arg
)
643 : malloc_diagnostic (sm
, arg
) {}
645 const char *get_kind () const FINAL OVERRIDE
{ return "null_deref"; }
647 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
649 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
650 diagnostic_metadata m
;
652 return warning_meta (rich_loc
, m
,
653 OPT_Wanalyzer_null_dereference
,
654 "dereference of NULL %qE", m_arg
);
657 label_text
describe_return_of_state (const evdesc::return_of_state
&info
)
660 if (info
.m_state
== m_sm
.m_null
)
661 return info
.formatted_print ("return of NULL to %qE from %qE",
662 info
.m_caller_fndecl
, info
.m_callee_fndecl
);
663 return label_text ();
666 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
668 return ev
.formatted_print ("dereference of NULL %qE", ev
.m_expr
);
672 /* Concrete subclass for describing passing a NULL value to a
673 function marked with __attribute__((nonnull)). */
675 class null_arg
: public malloc_diagnostic
678 null_arg (const malloc_state_machine
&sm
, tree arg
,
679 tree fndecl
, int arg_idx
)
680 : malloc_diagnostic (sm
, arg
),
681 m_fndecl (fndecl
), m_arg_idx (arg_idx
)
684 const char *get_kind () const FINAL OVERRIDE
{ return "null_arg"; }
686 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
688 const null_arg
&sub_other
689 = (const null_arg
&)base_other
;
690 return (same_tree_p (m_arg
, sub_other
.m_arg
)
691 && m_fndecl
== sub_other
.m_fndecl
692 && m_arg_idx
== sub_other
.m_arg_idx
);
695 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
697 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
698 auto_diagnostic_group d
;
699 diagnostic_metadata m
;
704 warned
= warning_meta (rich_loc
, m
, OPT_Wanalyzer_null_argument
,
705 "use of NULL where non-null expected");
707 warned
= warning_meta (rich_loc
, m
, OPT_Wanalyzer_null_argument
,
708 "use of NULL %qE where non-null expected",
711 inform_nonnull_attribute (m_fndecl
, m_arg_idx
);
715 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
717 if (zerop (ev
.m_expr
))
718 return ev
.formatted_print ("argument %u NULL where non-null expected",
721 return ev
.formatted_print ("argument %u (%qE) NULL"
722 " where non-null expected",
723 m_arg_idx
+ 1, ev
.m_expr
);
731 class use_after_free
: public malloc_diagnostic
734 use_after_free (const malloc_state_machine
&sm
, tree arg
,
736 : malloc_diagnostic (sm
, arg
), m_api (a
)
739 const char *get_kind () const FINAL OVERRIDE
{ return "use_after_free"; }
741 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
743 /* CWE-416: Use After Free. */
744 diagnostic_metadata m
;
746 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_use_after_free
,
747 "use after %<%s%> of %qE",
748 m_api
->m_dealloc_funcname
, m_arg
);
751 label_text
describe_state_change (const evdesc::state_change
&change
)
754 if (freed_p (change
.m_new_state
))
756 m_free_event
= change
.m_event_id
;
757 switch (m_api
->m_wording
)
762 return label_text::borrow ("freed here");
763 case WORDING_DELETED
:
764 return label_text::borrow ("deleted here");
767 return malloc_diagnostic::describe_state_change (change
);
770 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
772 const char *funcname
= m_api
->m_dealloc_funcname
;
773 if (m_free_event
.known_p ())
774 switch (m_api
->m_wording
)
779 return ev
.formatted_print ("use after %<%s%> of %qE; freed at %@",
780 funcname
, ev
.m_expr
, &m_free_event
);
781 case WORDING_DELETED
:
782 return ev
.formatted_print ("use after %<%s%> of %qE; deleted at %@",
783 funcname
, ev
.m_expr
, &m_free_event
);
786 return ev
.formatted_print ("use after %<%s%> of %qE",
787 funcname
, ev
.m_expr
);
791 diagnostic_event_id_t m_free_event
;
795 class malloc_leak
: public malloc_diagnostic
798 malloc_leak (const malloc_state_machine
&sm
, tree arg
)
799 : malloc_diagnostic (sm
, arg
) {}
801 const char *get_kind () const FINAL OVERRIDE
{ return "malloc_leak"; }
803 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
805 diagnostic_metadata m
;
808 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_malloc_leak
,
809 "leak of %qE", m_arg
);
811 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_malloc_leak
,
812 "leak of %qs", "<unknown>");
815 label_text
describe_state_change (const evdesc::state_change
&change
)
818 if (unchecked_p (change
.m_new_state
))
820 m_alloc_event
= change
.m_event_id
;
821 return label_text::borrow ("allocated here");
823 return malloc_diagnostic::describe_state_change (change
);
826 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
830 if (m_alloc_event
.known_p ())
831 return ev
.formatted_print ("%qE leaks here; was allocated at %@",
832 ev
.m_expr
, &m_alloc_event
);
834 return ev
.formatted_print ("%qE leaks here", ev
.m_expr
);
838 if (m_alloc_event
.known_p ())
839 return ev
.formatted_print ("%qs leaks here; was allocated at %@",
840 "<unknown>", &m_alloc_event
);
842 return ev
.formatted_print ("%qs leaks here", "<unknown>");
847 diagnostic_event_id_t m_alloc_event
;
850 class free_of_non_heap
: public malloc_diagnostic
853 free_of_non_heap (const malloc_state_machine
&sm
, tree arg
,
854 const char *funcname
)
855 : malloc_diagnostic (sm
, arg
), m_funcname (funcname
), m_kind (KIND_UNKNOWN
)
859 const char *get_kind () const FINAL OVERRIDE
{ return "free_of_non_heap"; }
861 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
864 const free_of_non_heap
&other
= (const free_of_non_heap
&)base_other
;
865 return (same_tree_p (m_arg
, other
.m_arg
) && m_kind
== other
.m_kind
);
868 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
870 auto_diagnostic_group d
;
871 diagnostic_metadata m
;
872 m
.add_cwe (590); /* CWE-590: Free of Memory not on the Heap. */
878 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_free_of_non_heap
,
879 "%<%s%> of %qE which points to memory"
884 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_free_of_non_heap
,
885 "%<%s%> of memory allocated on the stack by"
886 " %qs (%qE) will corrupt the heap",
887 m_funcname
, "alloca", m_arg
);
892 label_text
describe_state_change (const evdesc::state_change
&change
)
895 /* Attempt to reconstruct what kind of pointer it is.
896 (It seems neater for this to be a part of the state, though). */
897 if (TREE_CODE (change
.m_expr
) == SSA_NAME
)
899 gimple
*def_stmt
= SSA_NAME_DEF_STMT (change
.m_expr
);
900 if (gcall
*call
= dyn_cast
<gcall
*> (def_stmt
))
902 if (is_special_named_call_p (call
, "alloca", 1)
903 || is_special_named_call_p (call
, "__builtin_alloca", 1))
905 m_kind
= KIND_ALLOCA
;
906 return label_text::borrow
907 ("memory is allocated on the stack here");
911 return label_text::borrow ("pointer is from here");
914 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
916 return ev
.formatted_print ("call to %qs here", m_funcname
);
925 const char *m_funcname
;
929 /* struct allocation_state : public state_machine::state. */
931 /* Implementation of state_machine::state::dump_to_pp vfunc
932 for allocation_state: append the API that this allocation is
936 allocation_state::dump_to_pp (pretty_printer
*pp
) const
938 state_machine::state::dump_to_pp (pp
);
940 pp_printf (pp
, " (%s)", m_api
->m_name
);
943 /* Given a allocation_state for an api, get the "nonnull" state
944 for the corresponding allocator. */
946 const allocation_state
*
947 allocation_state::get_nonnull () const
950 return as_a_allocation_state (m_api
->m_nonnull
);
953 /* malloc_state_machine's ctor. */
955 malloc_state_machine::malloc_state_machine (logger
*logger
)
956 : state_machine ("malloc", logger
),
957 m_malloc (this, "malloc", "free", WORDING_FREED
),
958 m_scalar_new (this, "new", "delete", WORDING_DELETED
),
959 m_vector_new (this, "new[]", "delete[]", WORDING_DELETED
)
961 gcc_assert (m_start
->get_id () == 0);
962 m_null
= add_state ("null", RS_FREED
, NULL
);
963 m_non_heap
= add_state ("non-heap", RS_NON_HEAP
, NULL
);
964 m_stop
= add_state ("stop", RS_STOP
, NULL
);
967 state_machine::state_t
968 malloc_state_machine::add_state (const char *name
, enum resource_state rs
,
971 return add_custom_state (new allocation_state (name
, alloc_state_id (),
975 /* Implementation of state_machine::on_stmt vfunc for malloc_state_machine. */
978 malloc_state_machine::on_stmt (sm_context
*sm_ctxt
,
979 const supernode
*node
,
980 const gimple
*stmt
) const
982 if (const gcall
*call
= dyn_cast
<const gcall
*> (stmt
))
983 if (tree callee_fndecl
= sm_ctxt
->get_fndecl_for_call (call
))
985 if (is_named_call_p (callee_fndecl
, "malloc", call
, 1)
986 || is_named_call_p (callee_fndecl
, "calloc", call
, 2)
987 || is_std_named_call_p (callee_fndecl
, "malloc", call
, 1)
988 || is_std_named_call_p (callee_fndecl
, "calloc", call
, 2)
989 || is_named_call_p (callee_fndecl
, "__builtin_malloc", call
, 1)
990 || is_named_call_p (callee_fndecl
, "__builtin_calloc", call
, 2)
991 || is_named_call_p (callee_fndecl
, "strdup", call
, 1)
992 || is_named_call_p (callee_fndecl
, "strndup", call
, 2))
994 on_allocator_call (sm_ctxt
, call
, m_malloc
);
998 if (is_named_call_p (callee_fndecl
, "operator new", call
, 1))
999 on_allocator_call (sm_ctxt
, call
, m_scalar_new
);
1000 else if (is_named_call_p (callee_fndecl
, "operator new []", call
, 1))
1001 on_allocator_call (sm_ctxt
, call
, m_vector_new
);
1002 else if (is_named_call_p (callee_fndecl
, "operator delete", call
, 1)
1003 || is_named_call_p (callee_fndecl
, "operator delete", call
, 2))
1005 on_deallocator_call (sm_ctxt
, node
, call
, m_scalar_new
);
1008 else if (is_named_call_p (callee_fndecl
, "operator delete []", call
, 1))
1010 on_deallocator_call (sm_ctxt
, node
, call
, m_vector_new
);
1014 if (is_named_call_p (callee_fndecl
, "alloca", call
, 1)
1015 || is_named_call_p (callee_fndecl
, "__builtin_alloca", call
, 1))
1017 tree lhs
= gimple_call_lhs (call
);
1019 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
, m_non_heap
);
1023 if (is_named_call_p (callee_fndecl
, "free", call
, 1)
1024 || is_std_named_call_p (callee_fndecl
, "free", call
, 1)
1025 || is_named_call_p (callee_fndecl
, "__builtin_free", call
, 1))
1027 on_deallocator_call (sm_ctxt
, node
, call
, m_malloc
);
1031 /* Handle "__attribute__((nonnull))". */
1033 tree fntype
= TREE_TYPE (callee_fndecl
);
1034 bitmap nonnull_args
= get_nonnull_args (fntype
);
1037 for (unsigned i
= 0; i
< gimple_call_num_args (stmt
); i
++)
1039 tree arg
= gimple_call_arg (stmt
, i
);
1040 if (TREE_CODE (TREE_TYPE (arg
)) != POINTER_TYPE
)
1042 /* If we have a nonnull-args, and either all pointers, or just
1043 the specified pointers. */
1044 if (bitmap_empty_p (nonnull_args
)
1045 || bitmap_bit_p (nonnull_args
, i
))
1047 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1048 state_t state
= sm_ctxt
->get_state (stmt
, arg
);
1049 /* Can't use a switch as the states are non-const. */
1050 if (unchecked_p (state
))
1052 sm_ctxt
->warn (node
, stmt
, arg
,
1053 new possible_null_arg (*this, diag_arg
,
1056 const allocation_state
*astate
1057 = as_a_allocation_state (state
);
1058 sm_ctxt
->set_next_state (stmt
, arg
,
1059 astate
->get_nonnull ());
1061 else if (state
== m_null
)
1063 sm_ctxt
->warn (node
, stmt
, arg
,
1064 new null_arg (*this, diag_arg
,
1066 sm_ctxt
->set_next_state (stmt
, arg
, m_stop
);
1070 BITMAP_FREE (nonnull_args
);
1075 if (tree lhs
= sm_ctxt
->is_zero_assignment (stmt
))
1076 if (any_pointer_p (lhs
))
1077 on_zero_assignment (sm_ctxt
, stmt
,lhs
);
1079 /* If we have "LHS = &EXPR;" and EXPR is something other than a MEM_REF,
1080 transition LHS from start to non_heap.
1081 Doing it for ADDR_EXPR(MEM_REF()) is likely wrong, and can lead to
1082 unbounded chains of unmergeable sm-state on pointer arithmetic in loops
1083 when optimization is enabled. */
1084 if (const gassign
*assign_stmt
= dyn_cast
<const gassign
*> (stmt
))
1086 enum tree_code op
= gimple_assign_rhs_code (assign_stmt
);
1087 if (op
== ADDR_EXPR
)
1089 tree lhs
= gimple_assign_lhs (assign_stmt
);
1092 tree addr_expr
= gimple_assign_rhs1 (assign_stmt
);
1093 if (TREE_CODE (TREE_OPERAND (addr_expr
, 0)) != MEM_REF
)
1094 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
, m_non_heap
);
1099 /* Handle dereferences. */
1100 for (unsigned i
= 0; i
< gimple_num_ops (stmt
); i
++)
1102 tree op
= gimple_op (stmt
, i
);
1105 if (TREE_CODE (op
) == COMPONENT_REF
)
1106 op
= TREE_OPERAND (op
, 0);
1108 if (TREE_CODE (op
) == MEM_REF
)
1110 tree arg
= TREE_OPERAND (op
, 0);
1111 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1113 state_t state
= sm_ctxt
->get_state (stmt
, arg
);
1114 if (unchecked_p (state
))
1116 sm_ctxt
->warn (node
, stmt
, arg
,
1117 new possible_null_deref (*this, diag_arg
));
1118 const allocation_state
*astate
= as_a_allocation_state (state
);
1119 sm_ctxt
->set_next_state (stmt
, arg
, astate
->get_nonnull ());
1121 else if (state
== m_null
)
1123 sm_ctxt
->warn (node
, stmt
, arg
,
1124 new null_deref (*this, diag_arg
));
1125 sm_ctxt
->set_next_state (stmt
, arg
, m_stop
);
1127 else if (freed_p (state
))
1129 const allocation_state
*astate
= as_a_allocation_state (state
);
1130 sm_ctxt
->warn (node
, stmt
, arg
,
1131 new use_after_free (*this, diag_arg
,
1133 sm_ctxt
->set_next_state (stmt
, arg
, m_stop
);
1140 /* Handle a call to an allocator. */
1143 malloc_state_machine::on_allocator_call (sm_context
*sm_ctxt
,
1145 const api
&ap
) const
1147 tree lhs
= gimple_call_lhs (call
);
1150 if (sm_ctxt
->get_state (call
, lhs
) == m_start
)
1151 sm_ctxt
->set_next_state (call
, lhs
, ap
.m_unchecked
);
1155 /* TODO: report leak. */
1160 malloc_state_machine::on_deallocator_call (sm_context
*sm_ctxt
,
1161 const supernode
*node
,
1163 const api
&ap
) const
1165 tree arg
= gimple_call_arg (call
, 0);
1166 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1168 state_t state
= sm_ctxt
->get_state (call
, arg
);
1170 /* start/unchecked/nonnull -> freed. */
1171 if (state
== m_start
)
1172 sm_ctxt
->set_next_state (call
, arg
, ap
.m_freed
);
1173 else if (unchecked_p (state
) || nonnull_p (state
))
1175 const allocation_state
*astate
= as_a_allocation_state (state
);
1177 if (astate
->m_api
!= &ap
)
1179 /* Wrong allocator. */
1180 pending_diagnostic
*d
1181 = new mismatching_deallocation (*this, diag_arg
,
1182 astate
->m_api
, &ap
);
1183 sm_ctxt
->warn (node
, call
, arg
, d
);
1185 sm_ctxt
->set_next_state (call
, arg
, ap
.m_freed
);
1188 /* Keep state "null" as-is, rather than transitioning to "freed";
1189 we don't want to complain about double-free of NULL. */
1191 else if (state
== ap
.m_freed
)
1193 /* freed -> stop, with warning. */
1194 sm_ctxt
->warn (node
, call
, arg
,
1195 new double_free (*this, diag_arg
,
1196 ap
.m_dealloc_funcname
));
1197 sm_ctxt
->set_next_state (call
, arg
, m_stop
);
1199 else if (state
== m_non_heap
)
1201 /* non-heap -> stop, with warning. */
1202 sm_ctxt
->warn (node
, call
, arg
,
1203 new free_of_non_heap (*this, diag_arg
,
1204 ap
.m_dealloc_funcname
));
1205 sm_ctxt
->set_next_state (call
, arg
, m_stop
);
1209 /* Implementation of state_machine::on_phi vfunc for malloc_state_machine. */
1212 malloc_state_machine::on_phi (sm_context
*sm_ctxt
,
1213 const supernode
*node ATTRIBUTE_UNUSED
,
1219 tree lhs
= gimple_phi_result (phi
);
1220 on_zero_assignment (sm_ctxt
, phi
, lhs
);
1224 /* Implementation of state_machine::on_condition vfunc for malloc_state_machine.
1225 Potentially transition state 'unchecked' to 'nonnull' or to 'null'. */
1228 malloc_state_machine::on_condition (sm_context
*sm_ctxt
,
1229 const supernode
*node ATTRIBUTE_UNUSED
,
1238 if (!any_pointer_p (lhs
))
1240 if (!any_pointer_p (rhs
))
1245 log ("got 'ARG != 0' match");
1246 state_t s
= sm_ctxt
->get_state (stmt
, lhs
);
1247 if (unchecked_p (s
))
1249 const allocation_state
*astate
= as_a_allocation_state (s
);
1250 sm_ctxt
->set_next_state (stmt
, lhs
, astate
->get_nonnull ());
1253 else if (op
== EQ_EXPR
)
1255 log ("got 'ARG == 0' match");
1256 state_t s
= sm_ctxt
->get_state (stmt
, lhs
);
1257 if (unchecked_p (s
))
1258 sm_ctxt
->set_next_state (stmt
, lhs
, m_null
);
1262 /* Implementation of state_machine::can_purge_p vfunc for malloc_state_machine.
1263 Don't allow purging of pointers in state 'unchecked' or 'nonnull'
1264 (to avoid false leak reports). */
1267 malloc_state_machine::can_purge_p (state_t s
) const
1269 enum resource_state rs
= get_rs (s
);
1270 return rs
!= RS_UNCHECKED
&& rs
!= RS_NONNULL
;
1273 /* Implementation of state_machine::on_leak vfunc for malloc_state_machine
1274 (for complaining about leaks of pointers in state 'unchecked' and
1277 pending_diagnostic
*
1278 malloc_state_machine::on_leak (tree var
) const
1280 return new malloc_leak (*this, var
);
1283 /* Implementation of state_machine::reset_when_passed_to_unknown_fn_p vfunc
1284 for malloc_state_machine. */
1287 malloc_state_machine::reset_when_passed_to_unknown_fn_p (state_t s
,
1288 bool is_mutable
) const
1290 /* An on-stack ptr doesn't stop being stack-allocated when passed to an
1292 if (s
== m_non_heap
)
1295 /* Otherwise, pointers passed as non-const can be freed. */
1299 /* Shared logic for handling GIMPLE_ASSIGNs and GIMPLE_PHIs that
1300 assign zero to LHS. */
1303 malloc_state_machine::on_zero_assignment (sm_context
*sm_ctxt
,
1307 state_t s
= sm_ctxt
->get_state (stmt
, lhs
);
1308 enum resource_state rs
= get_rs (s
);
1310 || rs
== RS_UNCHECKED
1313 sm_ctxt
->set_next_state (stmt
, lhs
, m_null
);
1316 } // anonymous namespace
1318 /* Internal interface to this file. */
1321 make_malloc_state_machine (logger
*logger
)
1323 return new malloc_state_machine (logger
);
1328 #endif /* #if ENABLE_ANALYZER */