1 /* A state machine for detecting misuses of the malloc/free API.
2 Copyright (C) 2019-2022 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"
45 #include "stringpool.h"
47 #include "analyzer/function-set.h"
48 #include "analyzer/program-state.h"
56 /* This state machine and its various support classes track allocations
59 It has a few standard allocation/deallocation pairs (e.g. new/delete),
60 and also supports user-defined ones via
61 __attribute__ ((malloc(DEALLOCATOR))).
63 There can be more than one valid deallocator for a given allocator,
65 __attribute__ ((malloc (fclose)))
66 __attribute__ ((malloc (freopen, 3)))
67 FILE* fopen (const char*, const char*);
68 A deallocator_set represents a particular set of valid deallocators.
70 We track the expected deallocator_set for a value, but not the allocation
71 function - there could be more than one allocator per deallocator_set.
72 For example, there could be dozens of allocators for "free" beyond just
73 malloc e.g. calloc, xstrdup, etc. We don't want to explode the number
74 of states by tracking individual allocators in the exploded graph;
75 we merely want to track "this value expects to have 'free' called on it".
76 Perhaps we can reconstruct which allocator was used later, when emitting
77 the path, if it's necessary for precision of wording of diagnostics. */
80 class deallocator_set
;
81 class malloc_state_machine
;
83 /* An enum for discriminating between different kinds of allocation_state. */
87 /* States that are independent of allocator/deallocator. */
89 /* The start state. */
92 /* State for a pointer that's known to be NULL. */
95 /* State for a pointer that's known to not be on the heap (e.g. to a local
99 /* Stop state, for pointers we don't want to track any more. */
102 /* States that relate to a specific deallocator_set. */
104 /* State for a pointer returned from an allocator that hasn't
105 been checked for NULL.
106 It could be a pointer to heap-allocated memory, or could be NULL. */
109 /* State for a pointer returned from an allocator,
110 known to be non-NULL. */
113 /* State for a pointer passed to a deallocator. */
117 /* Custom state subclass, which can optionally refer to an a
120 struct allocation_state
: public state_machine::state
122 allocation_state (const char *name
, unsigned id
,
123 enum resource_state rs
,
124 const deallocator_set
*deallocators
,
125 const deallocator
*deallocator
)
126 : state (name
, id
), m_rs (rs
),
127 m_deallocators (deallocators
),
128 m_deallocator (deallocator
)
131 void dump_to_pp (pretty_printer
*pp
) const FINAL OVERRIDE
;
133 const allocation_state
*get_nonnull () const;
135 enum resource_state m_rs
;
136 const deallocator_set
*m_deallocators
;
137 const deallocator
*m_deallocator
;
140 /* An enum for choosing which wording to use in various diagnostics
141 when describing deallocations. */
151 /* Base class representing a deallocation function,
152 either a built-in one we know about, or one exposed via
153 __attribute__((malloc(DEALLOCATOR))). */
157 hashval_t
hash () const;
158 void dump_to_pp (pretty_printer
*pp
) const;
159 static int cmp (const deallocator
*a
, const deallocator
*b
);
160 static int cmp_ptr_ptr (const void *, const void *);
162 /* Name to use in diagnostics. */
165 /* Which wording to use in diagnostics. */
166 enum wording m_wording
;
168 /* State for a value passed to one of the deallocators. */
169 state_machine::state_t m_freed
;
172 deallocator (malloc_state_machine
*sm
,
174 enum wording wording
);
177 /* Subclass representing a predefined deallocator.
178 e.g. "delete []", without needing a specific FUNCTION_DECL
181 struct standard_deallocator
: public deallocator
183 standard_deallocator (malloc_state_machine
*sm
,
185 enum wording wording
);
188 /* Subclass representing a user-defined deallocator
189 via __attribute__((malloc(DEALLOCATOR))) given
190 a specific FUNCTION_DECL. */
192 struct custom_deallocator
: public deallocator
194 custom_deallocator (malloc_state_machine
*sm
,
195 tree deallocator_fndecl
,
196 enum wording wording
)
197 : deallocator (sm
, IDENTIFIER_POINTER (DECL_NAME (deallocator_fndecl
)),
203 /* Base class representing a set of possible deallocators.
204 Often this will be just a single deallocator, but some
205 allocators have multiple valid deallocators (e.g. the result of
206 "fopen" can be closed by either "fclose" or "freopen"). */
208 struct deallocator_set
210 deallocator_set (malloc_state_machine
*sm
,
211 enum wording wording
);
212 virtual ~deallocator_set () {}
214 virtual bool contains_p (const deallocator
*d
) const = 0;
215 virtual const deallocator
*maybe_get_single () const = 0;
216 virtual void dump_to_pp (pretty_printer
*pp
) const = 0;
219 /* Which wording to use in diagnostics. */
220 enum wording m_wording
;
222 /* Pointers to states.
223 These states are owned by the state_machine base class. */
225 /* State for an unchecked result from an allocator using this set. */
226 state_machine::state_t m_unchecked
;
228 /* State for a known non-NULL result from such an allocator. */
229 state_machine::state_t m_nonnull
;
232 /* Subclass of deallocator_set representing a set of deallocators
233 defined by one or more __attribute__((malloc(DEALLOCATOR))). */
235 struct custom_deallocator_set
: public deallocator_set
237 typedef const auto_vec
<const deallocator
*> *key_t
;
239 custom_deallocator_set (malloc_state_machine
*sm
,
240 const auto_vec
<const deallocator
*> *vec
,
242 //const char *dealloc_funcname,
244 enum wording wording
);
246 bool contains_p (const deallocator
*d
) const FINAL OVERRIDE
;
247 const deallocator
*maybe_get_single () const FINAL OVERRIDE
;
248 void dump_to_pp (pretty_printer
*pp
) const FINAL OVERRIDE
;
250 auto_vec
<const deallocator
*> m_deallocator_vec
;
253 /* Subclass of deallocator_set representing a set of deallocators
254 with a single standard_deallocator, e.g. "delete []". */
256 struct standard_deallocator_set
: public deallocator_set
258 standard_deallocator_set (malloc_state_machine
*sm
,
260 enum wording wording
);
262 bool contains_p (const deallocator
*d
) const FINAL OVERRIDE
;
263 const deallocator
*maybe_get_single () const FINAL OVERRIDE
;
264 void dump_to_pp (pretty_printer
*pp
) const FINAL OVERRIDE
;
266 standard_deallocator m_deallocator
;
269 /* Traits class for ensuring uniqueness of deallocator_sets within
270 malloc_state_machine. */
272 struct deallocator_set_map_traits
274 typedef custom_deallocator_set::key_t key_type
;
275 typedef custom_deallocator_set
*value_type
;
276 typedef custom_deallocator_set
*compare_type
;
278 static inline hashval_t
hash (const key_type
&k
)
280 gcc_assert (k
!= NULL
);
281 gcc_assert (k
!= reinterpret_cast<key_type
> (1));
283 hashval_t result
= 0;
285 const deallocator
*d
;
286 FOR_EACH_VEC_ELT (*k
, i
, d
)
287 result
^= d
->hash ();
290 static inline bool equal_keys (const key_type
&k1
, const key_type
&k2
)
292 if (k1
->length () != k2
->length ())
295 for (unsigned i
= 0; i
< k1
->length (); i
++)
296 if ((*k1
)[i
] != (*k2
)[i
])
301 template <typename T
>
302 static inline void remove (T
&)
304 /* empty; the nodes are handled elsewhere. */
306 template <typename T
>
307 static inline void mark_deleted (T
&entry
)
309 entry
.m_key
= reinterpret_cast<key_type
> (1);
311 template <typename T
>
312 static inline void mark_empty (T
&entry
)
316 template <typename T
>
317 static inline bool is_deleted (const T
&entry
)
319 return entry
.m_key
== reinterpret_cast<key_type
> (1);
321 template <typename T
>
322 static inline bool is_empty (const T
&entry
)
324 return entry
.m_key
== NULL
;
326 static const bool empty_zero_p
= false;
329 /* A state machine for detecting misuses of the malloc/free API.
331 See sm-malloc.dot for an overview (keep this in-sync with that file). */
333 class malloc_state_machine
: public state_machine
336 typedef allocation_state custom_data_t
;
338 malloc_state_machine (logger
*logger
);
339 ~malloc_state_machine ();
342 add_state (const char *name
, enum resource_state rs
,
343 const deallocator_set
*deallocators
,
344 const deallocator
*deallocator
);
346 bool inherited_state_p () const FINAL OVERRIDE
{ return false; }
348 state_machine::state_t
349 get_default_state (const svalue
*sval
) const FINAL OVERRIDE
351 if (tree cst
= sval
->maybe_get_constant ())
356 if (const region_svalue
*ptr
= sval
->dyn_cast_region_svalue ())
358 const region
*reg
= ptr
->get_pointee ();
359 switch (reg
->get_memory_space ())
364 case MEMSPACE_GLOBALS
:
366 case MEMSPACE_READONLY_DATA
:
373 bool on_stmt (sm_context
*sm_ctxt
,
374 const supernode
*node
,
375 const gimple
*stmt
) const FINAL OVERRIDE
;
377 void on_phi (sm_context
*sm_ctxt
,
378 const supernode
*node
,
380 tree rhs
) const FINAL OVERRIDE
;
382 void on_condition (sm_context
*sm_ctxt
,
383 const supernode
*node
,
387 const svalue
*rhs
) const FINAL OVERRIDE
;
389 bool can_purge_p (state_t s
) const FINAL OVERRIDE
;
390 pending_diagnostic
*on_leak (tree var
) const FINAL OVERRIDE
;
392 bool reset_when_passed_to_unknown_fn_p (state_t s
,
393 bool is_mutable
) const FINAL OVERRIDE
;
395 static bool unaffected_by_call_p (tree fndecl
);
397 void on_realloc_with_move (region_model
*model
,
399 const svalue
*old_ptr_sval
,
400 const svalue
*new_ptr_sval
,
401 const extrinsic_state
&ext_state
) const;
403 standard_deallocator_set m_free
;
404 standard_deallocator_set m_scalar_delete
;
405 standard_deallocator_set m_vector_delete
;
407 standard_deallocator m_realloc
;
409 /* States that are independent of api. */
411 /* State for a pointer that's known to be NULL. */
414 /* State for a pointer that's known to not be on the heap (e.g. to a local
416 state_t m_non_heap
; // TODO: or should this be a different state machine?
417 // or do we need child values etc?
419 /* Stop state, for pointers we don't want to track any more. */
423 const custom_deallocator_set
*
424 get_or_create_custom_deallocator_set (tree allocator_fndecl
);
425 custom_deallocator_set
*
426 maybe_create_custom_deallocator_set (tree allocator_fndecl
);
428 get_or_create_deallocator (tree deallocator_fndecl
);
430 void on_allocator_call (sm_context
*sm_ctxt
,
432 const deallocator_set
*deallocators
,
433 bool returns_nonnull
= false) const;
434 void handle_free_of_non_heap (sm_context
*sm_ctxt
,
435 const supernode
*node
,
438 const deallocator
*d
) const;
439 void on_deallocator_call (sm_context
*sm_ctxt
,
440 const supernode
*node
,
442 const deallocator
*d
,
443 unsigned argno
) const;
444 void on_realloc_call (sm_context
*sm_ctxt
,
445 const supernode
*node
,
446 const gcall
*call
) const;
447 void on_zero_assignment (sm_context
*sm_ctxt
,
451 /* A map for consolidating deallocators so that they are
452 unique per deallocator FUNCTION_DECL. */
453 typedef hash_map
<tree
, deallocator
*> deallocator_map_t
;
454 deallocator_map_t m_deallocator_map
;
456 /* Memoized lookups from FUNCTION_DECL to custom_deallocator_set *. */
457 typedef hash_map
<tree
, custom_deallocator_set
*> deallocator_set_cache_t
;
458 deallocator_set_cache_t m_custom_deallocator_set_cache
;
460 /* A map for consolidating custom_deallocator_set instances. */
461 typedef hash_map
<custom_deallocator_set::key_t
,
462 custom_deallocator_set
*,
463 deallocator_set_map_traits
> custom_deallocator_set_map_t
;
464 custom_deallocator_set_map_t m_custom_deallocator_set_map
;
466 /* Record of dynamically-allocated objects, for cleanup. */
467 auto_vec
<custom_deallocator_set
*> m_dynamic_sets
;
468 auto_vec
<custom_deallocator
*> m_dynamic_deallocators
;
471 /* struct deallocator. */
473 deallocator::deallocator (malloc_state_machine
*sm
,
475 enum wording wording
)
478 m_freed (sm
->add_state ("freed", RS_FREED
, NULL
, this))
483 deallocator::hash () const
485 return (hashval_t
)m_freed
->get_id ();
489 deallocator::dump_to_pp (pretty_printer
*pp
) const
491 pp_printf (pp
, "%qs", m_name
);
495 deallocator::cmp (const deallocator
*a
, const deallocator
*b
)
497 return (int)a
->m_freed
->get_id () - (int)b
->m_freed
->get_id ();
501 deallocator::cmp_ptr_ptr (const void *a
, const void *b
)
503 return cmp (*(const deallocator
* const *)a
,
504 *(const deallocator
* const *)b
);
508 /* struct standard_deallocator : public deallocator. */
510 standard_deallocator::standard_deallocator (malloc_state_machine
*sm
,
512 enum wording wording
)
513 : deallocator (sm
, name
, wording
)
517 /* struct deallocator_set. */
519 deallocator_set::deallocator_set (malloc_state_machine
*sm
,
520 enum wording wording
)
521 : m_wording (wording
),
522 m_unchecked (sm
->add_state ("unchecked", RS_UNCHECKED
, this, NULL
)),
523 m_nonnull (sm
->add_state ("nonnull", RS_NONNULL
, this, NULL
))
527 /* Dump a description of this deallocator_set to stderr. */
530 deallocator_set::dump () const
533 pp_show_color (&pp
) = pp_show_color (global_dc
->printer
);
534 pp
.buffer
->stream
= stderr
;
540 /* struct custom_deallocator_set : public deallocator_set. */
542 custom_deallocator_set::
543 custom_deallocator_set (malloc_state_machine
*sm
,
544 const auto_vec
<const deallocator
*> *vec
,
545 enum wording wording
)
546 : deallocator_set (sm
, wording
),
547 m_deallocator_vec (vec
->length ())
550 const deallocator
*d
;
551 FOR_EACH_VEC_ELT (*vec
, i
, d
)
552 m_deallocator_vec
.safe_push (d
);
556 custom_deallocator_set::contains_p (const deallocator
*d
) const
559 const deallocator
*cd
;
560 FOR_EACH_VEC_ELT (m_deallocator_vec
, i
, cd
)
567 custom_deallocator_set::maybe_get_single () const
569 if (m_deallocator_vec
.length () == 1)
570 return m_deallocator_vec
[0];
575 custom_deallocator_set::dump_to_pp (pretty_printer
*pp
) const
577 pp_character (pp
, '{');
579 const deallocator
*d
;
580 FOR_EACH_VEC_ELT (m_deallocator_vec
, i
, d
)
583 pp_string (pp
, ", ");
586 pp_character (pp
, '}');
589 /* struct standard_deallocator_set : public deallocator_set. */
591 standard_deallocator_set::standard_deallocator_set (malloc_state_machine
*sm
,
593 enum wording wording
)
594 : deallocator_set (sm
, wording
),
595 m_deallocator (sm
, name
, wording
)
600 standard_deallocator_set::contains_p (const deallocator
*d
) const
602 return d
== &m_deallocator
;
606 standard_deallocator_set::maybe_get_single () const
608 return &m_deallocator
;
612 standard_deallocator_set::dump_to_pp (pretty_printer
*pp
) const
614 pp_character (pp
, '{');
615 pp_string (pp
, m_deallocator
.m_name
);
616 pp_character (pp
, '}');
619 /* Return STATE cast to the custom state subclass, or NULL for the start state.
620 Everything should be an allocation_state apart from the start state. */
622 static const allocation_state
*
623 dyn_cast_allocation_state (state_machine::state_t state
)
625 if (state
->get_id () == 0)
627 return static_cast <const allocation_state
*> (state
);
630 /* Return STATE cast to the custom state subclass, for a state that is
631 already known to not be the start state . */
633 static const allocation_state
*
634 as_a_allocation_state (state_machine::state_t state
)
636 gcc_assert (state
->get_id () != 0);
637 return static_cast <const allocation_state
*> (state
);
640 /* Get the resource_state for STATE. */
642 static enum resource_state
643 get_rs (state_machine::state_t state
)
645 if (const allocation_state
*astate
= dyn_cast_allocation_state (state
))
651 /* Return true if STATE is the start state. */
654 start_p (state_machine::state_t state
)
656 return get_rs (state
) == RS_START
;
659 /* Return true if STATE is an unchecked result from an allocator. */
662 unchecked_p (state_machine::state_t state
)
664 return get_rs (state
) == RS_UNCHECKED
;
667 /* Return true if STATE is a non-null result from an allocator. */
670 nonnull_p (state_machine::state_t state
)
672 return get_rs (state
) == RS_NONNULL
;
675 /* Return true if STATE is a value that has been passed to a deallocator. */
678 freed_p (state_machine::state_t state
)
680 return get_rs (state
) == RS_FREED
;
683 /* Class for diagnostics relating to malloc_state_machine. */
685 class malloc_diagnostic
: public pending_diagnostic
688 malloc_diagnostic (const malloc_state_machine
&sm
, tree arg
)
689 : m_sm (sm
), m_arg (arg
)
692 bool subclass_equal_p (const pending_diagnostic
&base_other
) const OVERRIDE
694 return same_tree_p (m_arg
, ((const malloc_diagnostic
&)base_other
).m_arg
);
697 label_text
describe_state_change (const evdesc::state_change
&change
)
700 if (change
.m_old_state
== m_sm
.get_start_state ()
701 && unchecked_p (change
.m_new_state
))
702 // TODO: verify that it's the allocation stmt, not a copy
703 return label_text::borrow ("allocated here");
704 if (unchecked_p (change
.m_old_state
)
705 && nonnull_p (change
.m_new_state
))
708 return change
.formatted_print ("assuming %qE is non-NULL",
711 return change
.formatted_print ("assuming %qs is non-NULL",
714 if (change
.m_new_state
== m_sm
.m_null
)
716 if (unchecked_p (change
.m_old_state
))
719 return change
.formatted_print ("assuming %qE is NULL",
722 return change
.formatted_print ("assuming %qs is NULL",
728 return change
.formatted_print ("%qE is NULL",
731 return change
.formatted_print ("%qs is NULL",
736 return label_text ();
740 const malloc_state_machine
&m_sm
;
744 /* Concrete subclass for reporting mismatching allocator/deallocator
747 class mismatching_deallocation
: public malloc_diagnostic
750 mismatching_deallocation (const malloc_state_machine
&sm
, tree arg
,
751 const deallocator_set
*expected_deallocators
,
752 const deallocator
*actual_dealloc
)
753 : malloc_diagnostic (sm
, arg
),
754 m_expected_deallocators (expected_deallocators
),
755 m_actual_dealloc (actual_dealloc
)
758 const char *get_kind () const FINAL OVERRIDE
760 return "mismatching_deallocation";
763 int get_controlling_option () const FINAL OVERRIDE
765 return OPT_Wanalyzer_mismatching_deallocation
;
768 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
770 auto_diagnostic_group d
;
771 diagnostic_metadata m
;
772 m
.add_cwe (762); /* CWE-762: Mismatched Memory Management Routines. */
773 if (const deallocator
*expected_dealloc
774 = m_expected_deallocators
->maybe_get_single ())
775 return warning_meta (rich_loc
, m
, get_controlling_option (),
776 "%qE should have been deallocated with %qs"
777 " but was deallocated with %qs",
778 m_arg
, expected_dealloc
->m_name
,
779 m_actual_dealloc
->m_name
);
781 return warning_meta (rich_loc
, m
, get_controlling_option (),
782 "%qs called on %qE returned from a mismatched"
783 " allocation function",
784 m_actual_dealloc
->m_name
, m_arg
);
787 label_text
describe_state_change (const evdesc::state_change
&change
)
790 if (unchecked_p (change
.m_new_state
))
792 m_alloc_event
= change
.m_event_id
;
793 if (const deallocator
*expected_dealloc
794 = m_expected_deallocators
->maybe_get_single ())
795 return change
.formatted_print ("allocated here"
796 " (expects deallocation with %qs)",
797 expected_dealloc
->m_name
);
799 return change
.formatted_print ("allocated here");
801 return malloc_diagnostic::describe_state_change (change
);
804 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
806 if (m_alloc_event
.known_p ())
808 if (const deallocator
*expected_dealloc
809 = m_expected_deallocators
->maybe_get_single ())
810 return ev
.formatted_print
811 ("deallocated with %qs here;"
812 " allocation at %@ expects deallocation with %qs",
813 m_actual_dealloc
->m_name
, &m_alloc_event
,
814 expected_dealloc
->m_name
);
816 return ev
.formatted_print
817 ("deallocated with %qs here;"
819 m_actual_dealloc
->m_name
, &m_alloc_event
);
821 return ev
.formatted_print ("deallocated with %qs here",
822 m_actual_dealloc
->m_name
);
826 diagnostic_event_id_t m_alloc_event
;
827 const deallocator_set
*m_expected_deallocators
;
828 const deallocator
*m_actual_dealloc
;
831 /* Concrete subclass for reporting double-free diagnostics. */
833 class double_free
: public malloc_diagnostic
836 double_free (const malloc_state_machine
&sm
, tree arg
, const char *funcname
)
837 : malloc_diagnostic (sm
, arg
), m_funcname (funcname
)
840 const char *get_kind () const FINAL OVERRIDE
{ return "double_free"; }
842 int get_controlling_option () const FINAL OVERRIDE
844 return OPT_Wanalyzer_double_free
;
847 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
849 auto_diagnostic_group d
;
850 diagnostic_metadata m
;
851 m
.add_cwe (415); /* CWE-415: Double Free. */
852 return warning_meta (rich_loc
, m
, get_controlling_option (),
853 "double-%qs of %qE", m_funcname
, m_arg
);
856 label_text
describe_state_change (const evdesc::state_change
&change
)
859 if (freed_p (change
.m_new_state
))
861 m_first_free_event
= change
.m_event_id
;
862 return change
.formatted_print ("first %qs here", m_funcname
);
864 return malloc_diagnostic::describe_state_change (change
);
867 label_text
describe_call_with_state (const evdesc::call_with_state
&info
)
870 if (freed_p (info
.m_state
))
871 return info
.formatted_print
872 ("passing freed pointer %qE in call to %qE from %qE",
873 info
.m_expr
, info
.m_callee_fndecl
, info
.m_caller_fndecl
);
874 return label_text ();
877 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
879 if (m_first_free_event
.known_p ())
880 return ev
.formatted_print ("second %qs here; first %qs was at %@",
881 m_funcname
, m_funcname
,
882 &m_first_free_event
);
883 return ev
.formatted_print ("second %qs here", m_funcname
);
887 diagnostic_event_id_t m_first_free_event
;
888 const char *m_funcname
;
891 /* Abstract subclass for describing possible bad uses of NULL.
892 Responsible for describing the call that could return NULL. */
894 class possible_null
: public malloc_diagnostic
897 possible_null (const malloc_state_machine
&sm
, tree arg
)
898 : malloc_diagnostic (sm
, arg
)
901 label_text
describe_state_change (const evdesc::state_change
&change
)
904 if (change
.m_old_state
== m_sm
.get_start_state ()
905 && unchecked_p (change
.m_new_state
))
907 m_origin_of_unchecked_event
= change
.m_event_id
;
908 return label_text::borrow ("this call could return NULL");
910 return malloc_diagnostic::describe_state_change (change
);
913 label_text
describe_return_of_state (const evdesc::return_of_state
&info
)
916 if (unchecked_p (info
.m_state
))
917 return info
.formatted_print ("possible return of NULL to %qE from %qE",
918 info
.m_caller_fndecl
, info
.m_callee_fndecl
);
919 return label_text ();
923 diagnostic_event_id_t m_origin_of_unchecked_event
;
926 /* Concrete subclass for describing dereference of a possible NULL
929 class possible_null_deref
: public possible_null
932 possible_null_deref (const malloc_state_machine
&sm
, tree arg
)
933 : possible_null (sm
, arg
)
936 const char *get_kind () const FINAL OVERRIDE
{ return "possible_null_deref"; }
938 int get_controlling_option () const FINAL OVERRIDE
940 return OPT_Wanalyzer_possible_null_dereference
;
943 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
945 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
946 diagnostic_metadata m
;
948 return warning_meta (rich_loc
, m
, get_controlling_option (),
949 "dereference of possibly-NULL %qE", m_arg
);
952 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
954 if (m_origin_of_unchecked_event
.known_p ())
955 return ev
.formatted_print ("%qE could be NULL: unchecked value from %@",
957 &m_origin_of_unchecked_event
);
959 return ev
.formatted_print ("%qE could be NULL", ev
.m_expr
);
964 /* Return true if FNDECL is a C++ method. */
967 method_p (tree fndecl
)
969 return TREE_CODE (TREE_TYPE (fndecl
)) == METHOD_TYPE
;
972 /* Return a 1-based description of ARG_IDX (0-based) of FNDECL.
973 Compare with %P in the C++ FE (implemented in cp/error.cc: parm_to_string
974 as called from cp_printer). */
977 describe_argument_index (tree fndecl
, int arg_idx
)
979 if (method_p (fndecl
))
981 return label_text::borrow ("'this'");
983 pp_printf (&pp
, "%u", arg_idx
+ 1 - method_p (fndecl
));
984 return label_text::take (xstrdup (pp_formatted_text (&pp
)));
987 /* Subroutine for use by possible_null_arg::emit and null_arg::emit.
988 Issue a note informing that the pertinent argument must be non-NULL. */
991 inform_nonnull_attribute (tree fndecl
, int arg_idx
)
993 label_text arg_desc
= describe_argument_index (fndecl
, arg_idx
);
994 inform (DECL_SOURCE_LOCATION (fndecl
),
995 "argument %s of %qD must be non-null",
996 arg_desc
.m_buffer
, fndecl
);
997 arg_desc
.maybe_free ();
998 /* Ideally we would use the location of the parm and underline the
999 attribute also - but we don't have the location_t values at this point
1001 For reference, the C and C++ FEs have get_fndecl_argument_location. */
1004 /* Concrete subclass for describing passing a possibly-NULL value to a
1005 function marked with __attribute__((nonnull)). */
1007 class possible_null_arg
: public possible_null
1010 possible_null_arg (const malloc_state_machine
&sm
, tree arg
,
1011 tree fndecl
, int arg_idx
)
1012 : possible_null (sm
, arg
),
1013 m_fndecl (fndecl
), m_arg_idx (arg_idx
)
1016 const char *get_kind () const FINAL OVERRIDE
{ return "possible_null_arg"; }
1018 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
1020 const possible_null_arg
&sub_other
1021 = (const possible_null_arg
&)base_other
;
1022 return (same_tree_p (m_arg
, sub_other
.m_arg
)
1023 && m_fndecl
== sub_other
.m_fndecl
1024 && m_arg_idx
== sub_other
.m_arg_idx
);
1027 int get_controlling_option () const FINAL OVERRIDE
1029 return OPT_Wanalyzer_possible_null_argument
;
1032 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
1034 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
1035 auto_diagnostic_group d
;
1036 diagnostic_metadata m
;
1039 = warning_meta (rich_loc
, m
, get_controlling_option (),
1040 "use of possibly-NULL %qE where non-null expected",
1043 inform_nonnull_attribute (m_fndecl
, m_arg_idx
);
1047 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
1049 label_text arg_desc
= describe_argument_index (m_fndecl
, m_arg_idx
);
1051 if (m_origin_of_unchecked_event
.known_p ())
1052 result
= ev
.formatted_print ("argument %s (%qE) from %@ could be NULL"
1053 " where non-null expected",
1054 arg_desc
.m_buffer
, ev
.m_expr
,
1055 &m_origin_of_unchecked_event
);
1057 result
= ev
.formatted_print ("argument %s (%qE) could be NULL"
1058 " where non-null expected",
1059 arg_desc
.m_buffer
, ev
.m_expr
);
1060 arg_desc
.maybe_free ();
1069 /* Concrete subclass for describing a dereference of a NULL value. */
1071 class null_deref
: public malloc_diagnostic
1074 null_deref (const malloc_state_machine
&sm
, tree arg
)
1075 : malloc_diagnostic (sm
, arg
) {}
1077 const char *get_kind () const FINAL OVERRIDE
{ return "null_deref"; }
1079 int get_controlling_option () const FINAL OVERRIDE
1081 return OPT_Wanalyzer_null_dereference
;
1084 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
1086 /* CWE-476: NULL Pointer Dereference. */
1087 diagnostic_metadata m
;
1089 return warning_meta (rich_loc
, m
, get_controlling_option (),
1090 "dereference of NULL %qE", m_arg
);
1093 label_text
describe_return_of_state (const evdesc::return_of_state
&info
)
1096 if (info
.m_state
== m_sm
.m_null
)
1097 return info
.formatted_print ("return of NULL to %qE from %qE",
1098 info
.m_caller_fndecl
, info
.m_callee_fndecl
);
1099 return label_text ();
1102 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
1104 return ev
.formatted_print ("dereference of NULL %qE", ev
.m_expr
);
1108 /* Concrete subclass for describing passing a NULL value to a
1109 function marked with __attribute__((nonnull)). */
1111 class null_arg
: public malloc_diagnostic
1114 null_arg (const malloc_state_machine
&sm
, tree arg
,
1115 tree fndecl
, int arg_idx
)
1116 : malloc_diagnostic (sm
, arg
),
1117 m_fndecl (fndecl
), m_arg_idx (arg_idx
)
1120 const char *get_kind () const FINAL OVERRIDE
{ return "null_arg"; }
1122 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
1124 const null_arg
&sub_other
1125 = (const null_arg
&)base_other
;
1126 return (same_tree_p (m_arg
, sub_other
.m_arg
)
1127 && m_fndecl
== sub_other
.m_fndecl
1128 && m_arg_idx
== sub_other
.m_arg_idx
);
1131 int get_controlling_option () const FINAL OVERRIDE
1133 return OPT_Wanalyzer_null_argument
;
1136 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
1138 /* CWE-476: NULL Pointer Dereference. */
1139 auto_diagnostic_group d
;
1140 diagnostic_metadata m
;
1145 warned
= warning_meta (rich_loc
, m
, get_controlling_option (),
1146 "use of NULL where non-null expected");
1148 warned
= warning_meta (rich_loc
, m
, get_controlling_option (),
1149 "use of NULL %qE where non-null expected",
1152 inform_nonnull_attribute (m_fndecl
, m_arg_idx
);
1156 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
1158 label_text arg_desc
= describe_argument_index (m_fndecl
, m_arg_idx
);
1160 if (zerop (ev
.m_expr
))
1161 result
= ev
.formatted_print ("argument %s NULL where non-null expected",
1164 result
= ev
.formatted_print ("argument %s (%qE) NULL"
1165 " where non-null expected",
1166 arg_desc
.m_buffer
, ev
.m_expr
);
1167 arg_desc
.maybe_free ();
1176 class use_after_free
: public malloc_diagnostic
1179 use_after_free (const malloc_state_machine
&sm
, tree arg
,
1180 const deallocator
*deallocator
)
1181 : malloc_diagnostic (sm
, arg
),
1182 m_deallocator (deallocator
)
1184 gcc_assert (deallocator
);
1187 const char *get_kind () const FINAL OVERRIDE
{ return "use_after_free"; }
1189 int get_controlling_option () const FINAL OVERRIDE
1191 return OPT_Wanalyzer_use_after_free
;
1194 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
1196 /* CWE-416: Use After Free. */
1197 diagnostic_metadata m
;
1199 return warning_meta (rich_loc
, m
, get_controlling_option (),
1200 "use after %<%s%> of %qE",
1201 m_deallocator
->m_name
, m_arg
);
1204 label_text
describe_state_change (const evdesc::state_change
&change
)
1207 if (freed_p (change
.m_new_state
))
1209 m_free_event
= change
.m_event_id
;
1210 switch (m_deallocator
->m_wording
)
1213 case WORDING_REALLOCATED
:
1216 return label_text::borrow ("freed here");
1217 case WORDING_DELETED
:
1218 return label_text::borrow ("deleted here");
1219 case WORDING_DEALLOCATED
:
1220 return label_text::borrow ("deallocated here");
1223 return malloc_diagnostic::describe_state_change (change
);
1226 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
1228 const char *funcname
= m_deallocator
->m_name
;
1229 if (m_free_event
.known_p ())
1230 switch (m_deallocator
->m_wording
)
1233 case WORDING_REALLOCATED
:
1236 return ev
.formatted_print ("use after %<%s%> of %qE; freed at %@",
1237 funcname
, ev
.m_expr
, &m_free_event
);
1238 case WORDING_DELETED
:
1239 return ev
.formatted_print ("use after %<%s%> of %qE; deleted at %@",
1240 funcname
, ev
.m_expr
, &m_free_event
);
1241 case WORDING_DEALLOCATED
:
1242 return ev
.formatted_print ("use after %<%s%> of %qE;"
1243 " deallocated at %@",
1244 funcname
, ev
.m_expr
, &m_free_event
);
1247 return ev
.formatted_print ("use after %<%s%> of %qE",
1248 funcname
, ev
.m_expr
);
1251 /* Implementation of pending_diagnostic::supercedes_p for
1254 We want use-after-free to supercede use-of-unitialized-value,
1255 so that if we have these at the same stmt, we don't emit
1256 a use-of-uninitialized, just the use-after-free.
1257 (this is because we fully purge information about freed
1258 buffers when we free them to avoid state explosions, so
1259 that if they are accessed after the free, it looks like
1260 they are uninitialized). */
1262 bool supercedes_p (const pending_diagnostic
&other
) const FINAL OVERRIDE
1264 if (other
.use_of_uninit_p ())
1271 diagnostic_event_id_t m_free_event
;
1272 const deallocator
*m_deallocator
;
1275 class malloc_leak
: public malloc_diagnostic
1278 malloc_leak (const malloc_state_machine
&sm
, tree arg
)
1279 : malloc_diagnostic (sm
, arg
) {}
1281 const char *get_kind () const FINAL OVERRIDE
{ return "malloc_leak"; }
1283 int get_controlling_option () const FINAL OVERRIDE
1285 return OPT_Wanalyzer_malloc_leak
;
1288 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
1290 diagnostic_metadata m
;
1293 return warning_meta (rich_loc
, m
, get_controlling_option (),
1294 "leak of %qE", m_arg
);
1296 return warning_meta (rich_loc
, m
, get_controlling_option (),
1297 "leak of %qs", "<unknown>");
1300 label_text
describe_state_change (const evdesc::state_change
&change
)
1303 if (unchecked_p (change
.m_new_state
)
1304 || (start_p (change
.m_old_state
) && nonnull_p (change
.m_new_state
)))
1306 m_alloc_event
= change
.m_event_id
;
1307 return label_text::borrow ("allocated here");
1309 return malloc_diagnostic::describe_state_change (change
);
1312 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
1316 if (m_alloc_event
.known_p ())
1317 return ev
.formatted_print ("%qE leaks here; was allocated at %@",
1318 ev
.m_expr
, &m_alloc_event
);
1320 return ev
.formatted_print ("%qE leaks here", ev
.m_expr
);
1324 if (m_alloc_event
.known_p ())
1325 return ev
.formatted_print ("%qs leaks here; was allocated at %@",
1326 "<unknown>", &m_alloc_event
);
1328 return ev
.formatted_print ("%qs leaks here", "<unknown>");
1333 diagnostic_event_id_t m_alloc_event
;
1336 class free_of_non_heap
: public malloc_diagnostic
1339 free_of_non_heap (const malloc_state_machine
&sm
, tree arg
,
1340 const region
*freed_reg
,
1341 const char *funcname
)
1342 : malloc_diagnostic (sm
, arg
), m_freed_reg (freed_reg
), m_funcname (funcname
)
1346 const char *get_kind () const FINAL OVERRIDE
{ return "free_of_non_heap"; }
1348 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
1351 const free_of_non_heap
&other
= (const free_of_non_heap
&)base_other
;
1352 return (same_tree_p (m_arg
, other
.m_arg
)
1353 && m_freed_reg
== other
.m_freed_reg
);
1356 int get_controlling_option () const FINAL OVERRIDE
1358 return OPT_Wanalyzer_free_of_non_heap
;
1361 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
1363 auto_diagnostic_group d
;
1364 diagnostic_metadata m
;
1365 m
.add_cwe (590); /* CWE-590: Free of Memory not on the Heap. */
1366 switch (get_memory_space ())
1371 case MEMSPACE_UNKNOWN
:
1373 case MEMSPACE_GLOBALS
:
1374 case MEMSPACE_READONLY_DATA
:
1375 return warning_meta (rich_loc
, m
, get_controlling_option (),
1376 "%<%s%> of %qE which points to memory"
1380 case MEMSPACE_STACK
:
1381 return warning_meta (rich_loc
, m
, get_controlling_option (),
1382 "%<%s%> of %qE which points to memory"
1389 label_text
describe_state_change (const evdesc::state_change
&)
1392 return label_text::borrow ("pointer is from here");
1395 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
1397 return ev
.formatted_print ("call to %qs here", m_funcname
);
1400 void mark_interesting_stuff (interesting_t
*interest
) FINAL OVERRIDE
1403 interest
->add_region_creation (m_freed_reg
);
1407 enum memory_space
get_memory_space () const
1410 return m_freed_reg
->get_memory_space ();
1412 return MEMSPACE_UNKNOWN
;
1415 const region
*m_freed_reg
;
1416 const char *m_funcname
;
1419 /* struct allocation_state : public state_machine::state. */
1421 /* Implementation of state_machine::state::dump_to_pp vfunc
1422 for allocation_state: append the API that this allocation is
1426 allocation_state::dump_to_pp (pretty_printer
*pp
) const
1428 state_machine::state::dump_to_pp (pp
);
1431 pp_string (pp
, " (");
1432 m_deallocators
->dump_to_pp (pp
);
1433 pp_character (pp
, ')');
1437 /* Given a allocation_state for a deallocator_set, get the "nonnull" state
1438 for the corresponding allocator(s). */
1440 const allocation_state
*
1441 allocation_state::get_nonnull () const
1443 gcc_assert (m_deallocators
);
1444 return as_a_allocation_state (m_deallocators
->m_nonnull
);
1447 /* malloc_state_machine's ctor. */
1449 malloc_state_machine::malloc_state_machine (logger
*logger
)
1450 : state_machine ("malloc", logger
),
1451 m_free (this, "free", WORDING_FREED
),
1452 m_scalar_delete (this, "delete", WORDING_DELETED
),
1453 m_vector_delete (this, "delete[]", WORDING_DELETED
),
1454 m_realloc (this, "realloc", WORDING_REALLOCATED
)
1456 gcc_assert (m_start
->get_id () == 0);
1457 m_null
= add_state ("null", RS_FREED
, NULL
, NULL
);
1458 m_non_heap
= add_state ("non-heap", RS_NON_HEAP
, NULL
, NULL
);
1459 m_stop
= add_state ("stop", RS_STOP
, NULL
, NULL
);
1462 malloc_state_machine::~malloc_state_machine ()
1465 custom_deallocator_set
*set
;
1466 FOR_EACH_VEC_ELT (m_dynamic_sets
, i
, set
)
1468 custom_deallocator
*d
;
1469 FOR_EACH_VEC_ELT (m_dynamic_deallocators
, i
, d
)
1473 state_machine::state_t
1474 malloc_state_machine::add_state (const char *name
, enum resource_state rs
,
1475 const deallocator_set
*deallocators
,
1476 const deallocator
*deallocator
)
1478 return add_custom_state (new allocation_state (name
, alloc_state_id (),
1483 /* If ALLOCATOR_FNDECL has any "__attribute__((malloc(FOO)))",
1484 return a custom_deallocator_set for them, consolidating them
1485 to ensure uniqueness of the sets.
1487 Return NULL if it has no such attributes. */
1489 const custom_deallocator_set
*
1490 malloc_state_machine::
1491 get_or_create_custom_deallocator_set (tree allocator_fndecl
)
1493 /* Early rejection of decls without attributes. */
1494 tree attrs
= DECL_ATTRIBUTES (allocator_fndecl
);
1498 /* Otherwise, call maybe_create_custom_deallocator_set,
1499 memoizing the result. */
1500 if (custom_deallocator_set
**slot
1501 = m_custom_deallocator_set_cache
.get (allocator_fndecl
))
1503 custom_deallocator_set
*set
1504 = maybe_create_custom_deallocator_set (allocator_fndecl
);
1505 m_custom_deallocator_set_cache
.put (allocator_fndecl
, set
);
1509 /* Given ALLOCATOR_FNDECL, a FUNCTION_DECL with attributes,
1510 look for any "__attribute__((malloc(FOO)))" and return a
1511 custom_deallocator_set for them, consolidating them
1512 to ensure uniqueness of the sets.
1514 Return NULL if it has no such attributes.
1516 Subroutine of get_or_create_custom_deallocator_set which
1517 memoizes the result. */
1519 custom_deallocator_set
*
1520 malloc_state_machine::
1521 maybe_create_custom_deallocator_set (tree allocator_fndecl
)
1523 tree attrs
= DECL_ATTRIBUTES (allocator_fndecl
);
1526 /* Look for instances of __attribute__((malloc(FOO))). */
1527 auto_vec
<const deallocator
*> deallocator_vec
;
1528 for (tree allocs
= attrs
;
1529 (allocs
= lookup_attribute ("malloc", allocs
));
1530 allocs
= TREE_CHAIN (allocs
))
1532 tree args
= TREE_VALUE (allocs
);
1535 if (TREE_VALUE (args
))
1537 const deallocator
*d
1538 = get_or_create_deallocator (TREE_VALUE (args
));
1539 deallocator_vec
.safe_push (d
);
1543 /* If there weren't any deallocators, bail. */
1544 if (deallocator_vec
.length () == 0)
1547 /* Consolidate, so that we reuse existing deallocator_set
1549 deallocator_vec
.qsort (deallocator::cmp_ptr_ptr
);
1550 custom_deallocator_set
**slot
1551 = m_custom_deallocator_set_map
.get (&deallocator_vec
);
1554 custom_deallocator_set
*set
1555 = new custom_deallocator_set (this, &deallocator_vec
, WORDING_DEALLOCATED
);
1556 m_custom_deallocator_set_map
.put (&set
->m_deallocator_vec
, set
);
1557 m_dynamic_sets
.safe_push (set
);
1561 /* Get the deallocator for DEALLOCATOR_FNDECL, creating it if necessary. */
1564 malloc_state_machine::get_or_create_deallocator (tree deallocator_fndecl
)
1566 deallocator
**slot
= m_deallocator_map
.get (deallocator_fndecl
);
1572 if (is_named_call_p (deallocator_fndecl
, "free")
1573 || is_std_named_call_p (deallocator_fndecl
, "free")
1574 || is_named_call_p (deallocator_fndecl
, "__builtin_free"))
1575 d
= &m_free
.m_deallocator
;
1578 custom_deallocator
*cd
1579 = new custom_deallocator (this, deallocator_fndecl
,
1580 WORDING_DEALLOCATED
);
1581 m_dynamic_deallocators
.safe_push (cd
);
1584 m_deallocator_map
.put (deallocator_fndecl
, d
);
1588 /* Try to identify the function declaration either by name or as a known malloc
1592 known_allocator_p (const_tree fndecl
, const gcall
*call
)
1594 /* Either it is a function we know by name and number of arguments... */
1595 if (is_named_call_p (fndecl
, "malloc", call
, 1)
1596 || is_named_call_p (fndecl
, "calloc", call
, 2)
1597 || is_std_named_call_p (fndecl
, "malloc", call
, 1)
1598 || is_std_named_call_p (fndecl
, "calloc", call
, 2)
1599 || is_named_call_p (fndecl
, "strdup", call
, 1)
1600 || is_named_call_p (fndecl
, "strndup", call
, 2))
1603 /* ... or it is a builtin allocator that allocates objects freed with
1605 if (fndecl_built_in_p (fndecl
, BUILT_IN_NORMAL
))
1606 switch (DECL_FUNCTION_CODE (fndecl
))
1608 case BUILT_IN_MALLOC
:
1609 case BUILT_IN_CALLOC
:
1610 case BUILT_IN_STRDUP
:
1611 case BUILT_IN_STRNDUP
:
1620 /* Implementation of state_machine::on_stmt vfunc for malloc_state_machine. */
1623 malloc_state_machine::on_stmt (sm_context
*sm_ctxt
,
1624 const supernode
*node
,
1625 const gimple
*stmt
) const
1627 if (const gcall
*call
= dyn_cast
<const gcall
*> (stmt
))
1628 if (tree callee_fndecl
= sm_ctxt
->get_fndecl_for_call (call
))
1630 if (known_allocator_p (callee_fndecl
, call
))
1632 on_allocator_call (sm_ctxt
, call
, &m_free
);
1636 if (is_named_call_p (callee_fndecl
, "operator new", call
, 1))
1637 on_allocator_call (sm_ctxt
, call
, &m_scalar_delete
);
1638 else if (is_named_call_p (callee_fndecl
, "operator new []", call
, 1))
1639 on_allocator_call (sm_ctxt
, call
, &m_vector_delete
);
1640 else if (is_named_call_p (callee_fndecl
, "operator delete", call
, 1)
1641 || is_named_call_p (callee_fndecl
, "operator delete", call
, 2))
1643 on_deallocator_call (sm_ctxt
, node
, call
,
1644 &m_scalar_delete
.m_deallocator
, 0);
1647 else if (is_named_call_p (callee_fndecl
, "operator delete []", call
, 1))
1649 on_deallocator_call (sm_ctxt
, node
, call
,
1650 &m_vector_delete
.m_deallocator
, 0);
1654 if (is_named_call_p (callee_fndecl
, "alloca", call
, 1)
1655 || is_named_call_p (callee_fndecl
, "__builtin_alloca", call
, 1))
1657 tree lhs
= gimple_call_lhs (call
);
1659 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
, m_non_heap
);
1663 if (is_named_call_p (callee_fndecl
, "free", call
, 1)
1664 || is_std_named_call_p (callee_fndecl
, "free", call
, 1)
1665 || is_named_call_p (callee_fndecl
, "__builtin_free", call
, 1))
1667 on_deallocator_call (sm_ctxt
, node
, call
,
1668 &m_free
.m_deallocator
, 0);
1672 if (is_named_call_p (callee_fndecl
, "realloc", call
, 2)
1673 || is_named_call_p (callee_fndecl
, "__builtin_realloc", call
, 2))
1675 on_realloc_call (sm_ctxt
, node
, call
);
1679 if (unaffected_by_call_p (callee_fndecl
))
1682 /* Cast away const-ness for cache-like operations. */
1683 malloc_state_machine
*mutable_this
1684 = const_cast <malloc_state_machine
*> (this);
1686 /* Handle "__attribute__((malloc(FOO)))". */
1687 if (const deallocator_set
*deallocators
1688 = mutable_this
->get_or_create_custom_deallocator_set
1691 tree attrs
= TYPE_ATTRIBUTES (TREE_TYPE (callee_fndecl
));
1692 bool returns_nonnull
1693 = lookup_attribute ("returns_nonnull", attrs
);
1694 on_allocator_call (sm_ctxt
, call
, deallocators
, returns_nonnull
);
1697 /* Handle "__attribute__((nonnull))". */
1699 tree fntype
= TREE_TYPE (callee_fndecl
);
1700 bitmap nonnull_args
= get_nonnull_args (fntype
);
1703 for (unsigned i
= 0; i
< gimple_call_num_args (stmt
); i
++)
1705 tree arg
= gimple_call_arg (stmt
, i
);
1706 if (TREE_CODE (TREE_TYPE (arg
)) != POINTER_TYPE
)
1708 /* If we have a nonnull-args, and either all pointers, or just
1709 the specified pointers. */
1710 if (bitmap_empty_p (nonnull_args
)
1711 || bitmap_bit_p (nonnull_args
, i
))
1713 state_t state
= sm_ctxt
->get_state (stmt
, arg
);
1714 /* Can't use a switch as the states are non-const. */
1715 if (unchecked_p (state
))
1717 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1718 sm_ctxt
->warn (node
, stmt
, arg
,
1719 new possible_null_arg (*this, diag_arg
,
1722 const allocation_state
*astate
1723 = as_a_allocation_state (state
);
1724 sm_ctxt
->set_next_state (stmt
, arg
,
1725 astate
->get_nonnull ());
1727 else if (state
== m_null
)
1729 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1730 sm_ctxt
->warn (node
, stmt
, arg
,
1731 new null_arg (*this, diag_arg
,
1733 sm_ctxt
->set_next_state (stmt
, arg
, m_stop
);
1737 BITMAP_FREE (nonnull_args
);
1741 /* Check for this after nonnull, so that if we have both
1742 then we transition to "freed", rather than "checked". */
1743 unsigned dealloc_argno
= fndecl_dealloc_argno (callee_fndecl
);
1744 if (dealloc_argno
!= UINT_MAX
)
1746 const deallocator
*d
1747 = mutable_this
->get_or_create_deallocator (callee_fndecl
);
1748 on_deallocator_call (sm_ctxt
, node
, call
, d
, dealloc_argno
);
1752 if (tree lhs
= sm_ctxt
->is_zero_assignment (stmt
))
1753 if (any_pointer_p (lhs
))
1754 on_zero_assignment (sm_ctxt
, stmt
,lhs
);
1756 /* Handle dereferences. */
1757 for (unsigned i
= 0; i
< gimple_num_ops (stmt
); i
++)
1759 tree op
= gimple_op (stmt
, i
);
1762 if (TREE_CODE (op
) == COMPONENT_REF
)
1763 op
= TREE_OPERAND (op
, 0);
1765 if (TREE_CODE (op
) == MEM_REF
)
1767 tree arg
= TREE_OPERAND (op
, 0);
1769 state_t state
= sm_ctxt
->get_state (stmt
, arg
);
1770 if (unchecked_p (state
))
1772 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1773 sm_ctxt
->warn (node
, stmt
, arg
,
1774 new possible_null_deref (*this, diag_arg
));
1775 const allocation_state
*astate
= as_a_allocation_state (state
);
1776 sm_ctxt
->set_next_state (stmt
, arg
, astate
->get_nonnull ());
1778 else if (state
== m_null
)
1780 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1781 sm_ctxt
->warn (node
, stmt
, arg
,
1782 new null_deref (*this, diag_arg
));
1783 sm_ctxt
->set_next_state (stmt
, arg
, m_stop
);
1785 else if (freed_p (state
))
1787 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1788 const allocation_state
*astate
= as_a_allocation_state (state
);
1789 sm_ctxt
->warn (node
, stmt
, arg
,
1790 new use_after_free (*this, diag_arg
,
1791 astate
->m_deallocator
));
1792 sm_ctxt
->set_next_state (stmt
, arg
, m_stop
);
1799 /* Handle a call to an allocator.
1800 RETURNS_NONNULL is true if CALL is to a fndecl known to have
1801 __attribute__((returns_nonnull)). */
1804 malloc_state_machine::on_allocator_call (sm_context
*sm_ctxt
,
1806 const deallocator_set
*deallocators
,
1807 bool returns_nonnull
) const
1809 tree lhs
= gimple_call_lhs (call
);
1812 if (sm_ctxt
->get_state (call
, lhs
) == m_start
)
1813 sm_ctxt
->set_next_state (call
, lhs
,
1815 ? deallocators
->m_nonnull
1816 : deallocators
->m_unchecked
));
1820 /* TODO: report leak. */
1824 /* Handle deallocations of non-heap pointers.
1825 non-heap -> stop, with warning. */
1828 malloc_state_machine::handle_free_of_non_heap (sm_context
*sm_ctxt
,
1829 const supernode
*node
,
1832 const deallocator
*d
) const
1834 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1835 const region
*freed_reg
= NULL
;
1836 if (const program_state
*old_state
= sm_ctxt
->get_old_program_state ())
1838 const region_model
*old_model
= old_state
->m_region_model
;
1839 const svalue
*ptr_sval
= old_model
->get_rvalue (arg
, NULL
);
1840 freed_reg
= old_model
->deref_rvalue (ptr_sval
, arg
, NULL
);
1842 sm_ctxt
->warn (node
, call
, arg
,
1843 new free_of_non_heap (*this, diag_arg
, freed_reg
,
1845 sm_ctxt
->set_next_state (call
, arg
, m_stop
);
1849 malloc_state_machine::on_deallocator_call (sm_context
*sm_ctxt
,
1850 const supernode
*node
,
1852 const deallocator
*d
,
1853 unsigned argno
) const
1855 if (argno
>= gimple_call_num_args (call
))
1857 tree arg
= gimple_call_arg (call
, argno
);
1859 state_t state
= sm_ctxt
->get_state (call
, arg
);
1861 /* start/unchecked/nonnull -> freed. */
1862 if (state
== m_start
)
1863 sm_ctxt
->set_next_state (call
, arg
, d
->m_freed
);
1864 else if (unchecked_p (state
) || nonnull_p (state
))
1866 const allocation_state
*astate
= as_a_allocation_state (state
);
1867 gcc_assert (astate
->m_deallocators
);
1868 if (!astate
->m_deallocators
->contains_p (d
))
1870 /* Wrong allocator. */
1871 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1872 pending_diagnostic
*pd
1873 = new mismatching_deallocation (*this, diag_arg
,
1874 astate
->m_deallocators
,
1876 sm_ctxt
->warn (node
, call
, arg
, pd
);
1878 sm_ctxt
->set_next_state (call
, arg
, d
->m_freed
);
1881 /* Keep state "null" as-is, rather than transitioning to "freed";
1882 we don't want to complain about double-free of NULL. */
1883 else if (state
== d
->m_freed
)
1885 /* freed -> stop, with warning. */
1886 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1887 sm_ctxt
->warn (node
, call
, arg
,
1888 new double_free (*this, diag_arg
, d
->m_name
));
1889 sm_ctxt
->set_next_state (call
, arg
, m_stop
);
1891 else if (state
== m_non_heap
)
1893 /* non-heap -> stop, with warning. */
1894 handle_free_of_non_heap (sm_ctxt
, node
, call
, arg
, d
);
1898 /* Handle a call to "realloc".
1899 Check for free of non-heap or mismatching allocators,
1900 transitioning to the "stop" state for such cases.
1902 Otherwise, region_model::impl_call_realloc will later
1903 get called (which will handle other sm-state transitions
1904 when the state is bifurcated). */
1907 malloc_state_machine::on_realloc_call (sm_context
*sm_ctxt
,
1908 const supernode
*node
,
1909 const gcall
*call
) const
1911 const unsigned argno
= 0;
1912 const deallocator
*d
= &m_realloc
;
1914 tree arg
= gimple_call_arg (call
, argno
);
1916 state_t state
= sm_ctxt
->get_state (call
, arg
);
1918 if (unchecked_p (state
) || nonnull_p (state
))
1920 const allocation_state
*astate
= as_a_allocation_state (state
);
1921 gcc_assert (astate
->m_deallocators
);
1922 if (!astate
->m_deallocators
->contains_p (&m_free
.m_deallocator
))
1924 /* Wrong allocator. */
1925 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1926 pending_diagnostic
*pd
1927 = new mismatching_deallocation (*this, diag_arg
,
1928 astate
->m_deallocators
,
1930 sm_ctxt
->warn (node
, call
, arg
, pd
);
1931 sm_ctxt
->set_next_state (call
, arg
, m_stop
);
1932 if (path_context
*path_ctxt
= sm_ctxt
->get_path_context ())
1933 path_ctxt
->terminate_path ();
1936 else if (state
== m_free
.m_deallocator
.m_freed
)
1938 /* freed -> stop, with warning. */
1939 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1940 sm_ctxt
->warn (node
, call
, arg
,
1941 new double_free (*this, diag_arg
, "free"));
1942 sm_ctxt
->set_next_state (call
, arg
, m_stop
);
1943 if (path_context
*path_ctxt
= sm_ctxt
->get_path_context ())
1944 path_ctxt
->terminate_path ();
1946 else if (state
== m_non_heap
)
1948 /* non-heap -> stop, with warning. */
1949 handle_free_of_non_heap (sm_ctxt
, node
, call
, arg
, d
);
1950 if (path_context
*path_ctxt
= sm_ctxt
->get_path_context ())
1951 path_ctxt
->terminate_path ();
1955 /* Implementation of state_machine::on_phi vfunc for malloc_state_machine. */
1958 malloc_state_machine::on_phi (sm_context
*sm_ctxt
,
1959 const supernode
*node ATTRIBUTE_UNUSED
,
1965 tree lhs
= gimple_phi_result (phi
);
1966 on_zero_assignment (sm_ctxt
, phi
, lhs
);
1970 /* Implementation of state_machine::on_condition vfunc for malloc_state_machine.
1971 Potentially transition state 'unchecked' to 'nonnull' or to 'null'. */
1974 malloc_state_machine::on_condition (sm_context
*sm_ctxt
,
1975 const supernode
*node ATTRIBUTE_UNUSED
,
1979 const svalue
*rhs
) const
1981 if (!rhs
->all_zeroes_p ())
1984 if (!any_pointer_p (lhs
))
1986 if (!any_pointer_p (rhs
))
1991 log ("got 'ARG != 0' match");
1992 state_t s
= sm_ctxt
->get_state (stmt
, lhs
);
1993 if (unchecked_p (s
))
1995 const allocation_state
*astate
= as_a_allocation_state (s
);
1996 sm_ctxt
->set_next_state (stmt
, lhs
, astate
->get_nonnull ());
1999 else if (op
== EQ_EXPR
)
2001 log ("got 'ARG == 0' match");
2002 state_t s
= sm_ctxt
->get_state (stmt
, lhs
);
2003 if (unchecked_p (s
))
2004 sm_ctxt
->set_next_state (stmt
, lhs
, m_null
);
2008 /* Implementation of state_machine::can_purge_p vfunc for malloc_state_machine.
2009 Don't allow purging of pointers in state 'unchecked' or 'nonnull'
2010 (to avoid false leak reports). */
2013 malloc_state_machine::can_purge_p (state_t s
) const
2015 enum resource_state rs
= get_rs (s
);
2016 return rs
!= RS_UNCHECKED
&& rs
!= RS_NONNULL
;
2019 /* Implementation of state_machine::on_leak vfunc for malloc_state_machine
2020 (for complaining about leaks of pointers in state 'unchecked' and
2023 pending_diagnostic
*
2024 malloc_state_machine::on_leak (tree var
) const
2026 return new malloc_leak (*this, var
);
2029 /* Implementation of state_machine::reset_when_passed_to_unknown_fn_p vfunc
2030 for malloc_state_machine. */
2033 malloc_state_machine::reset_when_passed_to_unknown_fn_p (state_t s
,
2034 bool is_mutable
) const
2036 /* An on-stack ptr doesn't stop being stack-allocated when passed to an
2038 if (s
== m_non_heap
)
2041 /* Otherwise, pointers passed as non-const can be freed. */
2045 /* Return true if calls to FNDECL are known to not affect this sm-state. */
2048 malloc_state_machine::unaffected_by_call_p (tree fndecl
)
2050 /* A set of functions that are known to not affect allocation
2051 status, even if we haven't fully modelled the rest of their
2053 static const char * const funcnames
[] = {
2054 /* This array must be kept sorted. */
2057 const size_t count
= ARRAY_SIZE (funcnames
);
2058 function_set
fs (funcnames
, count
);
2060 if (fs
.contains_decl_p (fndecl
))
2066 /* Shared logic for handling GIMPLE_ASSIGNs and GIMPLE_PHIs that
2067 assign zero to LHS. */
2070 malloc_state_machine::on_zero_assignment (sm_context
*sm_ctxt
,
2074 state_t s
= sm_ctxt
->get_state (stmt
, lhs
);
2075 enum resource_state rs
= get_rs (s
);
2077 || rs
== RS_UNCHECKED
2080 sm_ctxt
->set_next_state (stmt
, lhs
, m_null
);
2083 /* Special-case hook for handling realloc, for the "success with move to
2084 a new buffer" case, marking OLD_PTR_SVAL as freed and NEW_PTR_SVAL as
2087 This is similar to on_deallocator_call and on_allocator_call,
2088 but the checks happen in on_realloc_call, and by splitting the states. */
2091 malloc_state_machine::
2092 on_realloc_with_move (region_model
*model
,
2094 const svalue
*old_ptr_sval
,
2095 const svalue
*new_ptr_sval
,
2096 const extrinsic_state
&ext_state
) const
2098 smap
->set_state (model
, old_ptr_sval
,
2099 m_free
.m_deallocator
.m_freed
,
2102 smap
->set_state (model
, new_ptr_sval
,
2107 } // anonymous namespace
2109 /* Internal interface to this file. */
2112 make_malloc_state_machine (logger
*logger
)
2114 return new malloc_state_machine (logger
);
2117 /* Specialcase hook for handling realloc, for use by
2118 region_model::impl_call_realloc::success_with_move::update_model. */
2121 region_model::on_realloc_with_move (const call_details
&cd
,
2122 const svalue
*old_ptr_sval
,
2123 const svalue
*new_ptr_sval
)
2125 region_model_context
*ctxt
= cd
.get_ctxt ();
2128 const extrinsic_state
*ext_state
= ctxt
->get_ext_state ();
2133 const state_machine
*sm
;
2135 if (!ctxt
->get_malloc_map (&smap
, &sm
, &sm_idx
))
2141 const malloc_state_machine
&malloc_sm
2142 = (const malloc_state_machine
&)*sm
;
2144 malloc_sm
.on_realloc_with_move (this,
2153 #endif /* #if ENABLE_ANALYZER */