1 /* A state machine for detecting misuses of the malloc/free API.
2 Copyright (C) 2019-2024 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/>. */
22 #define INCLUDE_MEMORY
24 #include "coretypes.h"
25 #include "make-unique.h"
28 #include "basic-block.h"
32 #include "diagnostic-path.h"
33 #include "analyzer/analyzer.h"
34 #include "diagnostic-event-id.h"
35 #include "analyzer/analyzer-logging.h"
36 #include "analyzer/sm.h"
37 #include "analyzer/pending-diagnostic.h"
38 #include "analyzer/call-string.h"
39 #include "analyzer/program-point.h"
40 #include "analyzer/store.h"
41 #include "analyzer/region-model.h"
42 #include "analyzer/call-details.h"
43 #include "stringpool.h"
45 #include "analyzer/function-set.h"
46 #include "analyzer/program-state.h"
47 #include "analyzer/checker-event.h"
48 #include "analyzer/exploded-graph.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 been unconditionally dereferenced. */
95 /* State for a pointer that's known to be NULL. */
98 /* State for a pointer that's known to not be on the heap (e.g. to a local
102 /* Stop state, for pointers we don't want to track any more. */
105 /* States that relate to a specific deallocator_set. */
107 /* State for a pointer returned from an allocator that hasn't
108 been checked for NULL.
109 It could be a pointer to heap-allocated memory, or could be NULL. */
112 /* State for a pointer returned from an allocator,
113 known to be non-NULL. */
116 /* State for a pointer passed to a deallocator. */
120 /* Custom state subclass, which can optionally refer to an a
123 struct allocation_state
: public state_machine::state
125 allocation_state (const char *name
, unsigned id
,
126 enum resource_state rs
,
127 const deallocator_set
*deallocators
,
128 const deallocator
*deallocator
)
129 : state (name
, id
), m_rs (rs
),
130 m_deallocators (deallocators
),
131 m_deallocator (deallocator
)
134 void dump_to_pp (pretty_printer
*pp
) const override
;
136 const allocation_state
*get_nonnull () const;
138 enum resource_state m_rs
;
139 const deallocator_set
*m_deallocators
;
140 const deallocator
*m_deallocator
;
143 /* Custom state subclass, for the "assumed-non-null" state
144 where the assumption happens in a particular frame. */
146 struct assumed_non_null_state
: public allocation_state
148 assumed_non_null_state (const char *name
, unsigned id
,
149 const frame_region
*frame
)
150 : allocation_state (name
, id
, RS_ASSUMED_NON_NULL
,
154 gcc_assert (m_frame
);
157 void dump_to_pp (pretty_printer
*pp
) const final override
;
159 const frame_region
*m_frame
;
162 /* An enum for choosing which wording to use in various diagnostics
163 when describing deallocations. */
173 /* Base class representing a deallocation function,
174 either a built-in one we know about, or one exposed via
175 __attribute__((malloc(DEALLOCATOR))). */
179 hashval_t
hash () const;
180 void dump_to_pp (pretty_printer
*pp
) const;
181 static int cmp (const deallocator
*a
, const deallocator
*b
);
182 static int cmp_ptr_ptr (const void *, const void *);
184 /* Name to use in diagnostics. */
187 /* Which wording to use in diagnostics. */
188 enum wording m_wording
;
190 /* State for a value passed to one of the deallocators. */
191 state_machine::state_t m_freed
;
194 deallocator (malloc_state_machine
*sm
,
196 enum wording wording
);
199 /* Subclass representing a predefined deallocator.
200 e.g. "delete []", without needing a specific FUNCTION_DECL
203 struct standard_deallocator
: public deallocator
205 standard_deallocator (malloc_state_machine
*sm
,
207 enum wording wording
);
210 /* Subclass representing a user-defined deallocator
211 via __attribute__((malloc(DEALLOCATOR))) given
212 a specific FUNCTION_DECL. */
214 struct custom_deallocator
: public deallocator
216 custom_deallocator (malloc_state_machine
*sm
,
217 tree deallocator_fndecl
,
218 enum wording wording
)
219 : deallocator (sm
, IDENTIFIER_POINTER (DECL_NAME (deallocator_fndecl
)),
225 /* Base class representing a set of possible deallocators.
226 Often this will be just a single deallocator, but some
227 allocators have multiple valid deallocators (e.g. the result of
228 "fopen" can be closed by either "fclose" or "freopen"). */
230 struct deallocator_set
232 deallocator_set (malloc_state_machine
*sm
,
233 enum wording wording
);
234 virtual ~deallocator_set () {}
236 virtual bool contains_p (const deallocator
*d
) const = 0;
237 virtual const deallocator
*maybe_get_single () const = 0;
238 virtual void dump_to_pp (pretty_printer
*pp
) const = 0;
241 /* Which wording to use in diagnostics. */
242 enum wording m_wording
;
244 /* Pointers to states.
245 These states are owned by the state_machine base class. */
247 /* State for an unchecked result from an allocator using this set. */
248 state_machine::state_t m_unchecked
;
250 /* State for a known non-NULL result from such an allocator. */
251 state_machine::state_t m_nonnull
;
254 /* Subclass of deallocator_set representing a set of deallocators
255 defined by one or more __attribute__((malloc(DEALLOCATOR))). */
257 struct custom_deallocator_set
: public deallocator_set
259 typedef const auto_vec
<const deallocator
*> *key_t
;
261 custom_deallocator_set (malloc_state_machine
*sm
,
262 const auto_vec
<const deallocator
*> *vec
,
264 //const char *dealloc_funcname,
266 enum wording wording
);
268 bool contains_p (const deallocator
*d
) const final override
;
269 const deallocator
*maybe_get_single () const final override
;
270 void dump_to_pp (pretty_printer
*pp
) const final override
;
272 auto_vec
<const deallocator
*> m_deallocator_vec
;
275 /* Subclass of deallocator_set representing a set of deallocators
276 with a single standard_deallocator, e.g. "delete []". */
278 struct standard_deallocator_set
: public deallocator_set
280 standard_deallocator_set (malloc_state_machine
*sm
,
282 enum wording wording
);
284 bool contains_p (const deallocator
*d
) const final override
;
285 const deallocator
*maybe_get_single () const final override
;
286 void dump_to_pp (pretty_printer
*pp
) const final override
;
288 standard_deallocator m_deallocator
;
291 /* Traits class for ensuring uniqueness of deallocator_sets within
292 malloc_state_machine. */
294 struct deallocator_set_map_traits
296 typedef custom_deallocator_set::key_t key_type
;
297 typedef custom_deallocator_set
*value_type
;
298 typedef custom_deallocator_set
*compare_type
;
300 static inline hashval_t
hash (const key_type
&k
)
302 gcc_assert (k
!= NULL
);
303 gcc_assert (k
!= reinterpret_cast<key_type
> (1));
305 hashval_t result
= 0;
307 const deallocator
*d
;
308 FOR_EACH_VEC_ELT (*k
, i
, d
)
309 result
^= d
->hash ();
312 static inline bool equal_keys (const key_type
&k1
, const key_type
&k2
)
314 if (k1
->length () != k2
->length ())
317 for (unsigned i
= 0; i
< k1
->length (); i
++)
318 if ((*k1
)[i
] != (*k2
)[i
])
323 template <typename T
>
324 static inline void remove (T
&)
326 /* empty; the nodes are handled elsewhere. */
328 template <typename T
>
329 static inline void mark_deleted (T
&entry
)
331 entry
.m_key
= reinterpret_cast<key_type
> (1);
333 template <typename T
>
334 static inline void mark_empty (T
&entry
)
338 template <typename T
>
339 static inline bool is_deleted (const T
&entry
)
341 return entry
.m_key
== reinterpret_cast<key_type
> (1);
343 template <typename T
>
344 static inline bool is_empty (const T
&entry
)
346 return entry
.m_key
== NULL
;
348 static const bool empty_zero_p
= false;
351 /* A state machine for detecting misuses of the malloc/free API.
353 See sm-malloc.dot for an overview (keep this in-sync with that file). */
355 class malloc_state_machine
: public state_machine
358 typedef allocation_state custom_data_t
;
360 malloc_state_machine (logger
*logger
);
361 ~malloc_state_machine ();
364 add_state (const char *name
, enum resource_state rs
,
365 const deallocator_set
*deallocators
,
366 const deallocator
*deallocator
);
368 bool inherited_state_p () const final override
{ return false; }
370 state_machine::state_t
371 get_default_state (const svalue
*sval
) const final override
373 if (tree cst
= sval
->maybe_get_constant ())
378 if (const region_svalue
*ptr
= sval
->dyn_cast_region_svalue ())
380 const region
*reg
= ptr
->get_pointee ();
381 switch (reg
->get_memory_space ())
386 case MEMSPACE_GLOBALS
:
388 case MEMSPACE_READONLY_DATA
:
395 bool on_stmt (sm_context
*sm_ctxt
,
396 const supernode
*node
,
397 const gimple
*stmt
) const final override
;
399 void on_phi (sm_context
*sm_ctxt
,
400 const supernode
*node
,
402 tree rhs
) const final override
;
404 void on_condition (sm_context
*sm_ctxt
,
405 const supernode
*node
,
409 const svalue
*rhs
) const final override
;
411 void on_pop_frame (sm_state_map
*smap
,
412 const frame_region
*) const final override
;
414 bool can_purge_p (state_t s
) const final override
;
415 std::unique_ptr
<pending_diagnostic
> on_leak (tree var
) const final override
;
417 bool reset_when_passed_to_unknown_fn_p (state_t s
,
418 bool is_mutable
) const final override
;
421 maybe_get_merged_states_nonequal (state_t state_a
,
422 state_t state_b
) const final override
;
424 static bool unaffected_by_call_p (tree fndecl
);
426 void maybe_assume_non_null (sm_context
*sm_ctxt
,
428 const gimple
*stmt
) const;
430 void on_realloc_with_move (region_model
*model
,
432 const svalue
*old_ptr_sval
,
433 const svalue
*new_ptr_sval
,
434 const extrinsic_state
&ext_state
) const;
436 void transition_ptr_sval_non_null (region_model
*model
,
438 const svalue
*new_ptr_sval
,
439 const extrinsic_state
&ext_state
) const;
441 standard_deallocator_set m_free
;
442 standard_deallocator_set m_scalar_delete
;
443 standard_deallocator_set m_vector_delete
;
445 standard_deallocator m_realloc
;
447 /* States that are independent of api. */
449 /* States for a pointer that's been unconditionally dereferenced
450 in a particular stack frame. */
451 hash_map
<const frame_region
*, state_t
> m_assumed_non_null
;
453 /* State for a pointer that's known to be NULL. */
456 /* State for a pointer that's known to not be on the heap (e.g. to a local
458 state_t m_non_heap
; // TODO: or should this be a different state machine?
459 // or do we need child values etc?
461 /* Stop state, for pointers we don't want to track any more. */
465 const custom_deallocator_set
*
466 get_or_create_custom_deallocator_set (tree allocator_fndecl
);
467 custom_deallocator_set
*
468 maybe_create_custom_deallocator_set (tree allocator_fndecl
);
470 get_or_create_deallocator (tree deallocator_fndecl
);
473 get_or_create_assumed_non_null_state_for_frame (const frame_region
*frame
);
476 maybe_complain_about_deref_before_check (sm_context
*sm_ctxt
,
477 const supernode
*node
,
479 const assumed_non_null_state
*,
482 void on_allocator_call (sm_context
*sm_ctxt
,
484 const deallocator_set
*deallocators
,
485 bool returns_nonnull
= false) const;
486 void handle_free_of_non_heap (sm_context
*sm_ctxt
,
487 const supernode
*node
,
490 const deallocator
*d
) const;
491 void on_deallocator_call (sm_context
*sm_ctxt
,
492 const supernode
*node
,
494 const deallocator
*d
,
495 unsigned argno
) const;
496 void on_realloc_call (sm_context
*sm_ctxt
,
497 const supernode
*node
,
498 const gcall
*call
) const;
499 void on_zero_assignment (sm_context
*sm_ctxt
,
503 /* A map for consolidating deallocators so that they are
504 unique per deallocator FUNCTION_DECL. */
505 typedef hash_map
<tree
, deallocator
*> deallocator_map_t
;
506 deallocator_map_t m_deallocator_map
;
508 /* Memoized lookups from FUNCTION_DECL to custom_deallocator_set *. */
509 typedef hash_map
<tree
, custom_deallocator_set
*> deallocator_set_cache_t
;
510 deallocator_set_cache_t m_custom_deallocator_set_cache
;
512 /* A map for consolidating custom_deallocator_set instances. */
513 typedef hash_map
<custom_deallocator_set::key_t
,
514 custom_deallocator_set
*,
515 deallocator_set_map_traits
> custom_deallocator_set_map_t
;
516 custom_deallocator_set_map_t m_custom_deallocator_set_map
;
518 /* Record of dynamically-allocated objects, for cleanup. */
519 auto_vec
<custom_deallocator_set
*> m_dynamic_sets
;
520 auto_vec
<custom_deallocator
*> m_dynamic_deallocators
;
523 /* struct deallocator. */
525 deallocator::deallocator (malloc_state_machine
*sm
,
527 enum wording wording
)
530 m_freed (sm
->add_state ("freed", RS_FREED
, NULL
, this))
535 deallocator::hash () const
537 return (hashval_t
)m_freed
->get_id ();
541 deallocator::dump_to_pp (pretty_printer
*pp
) const
543 pp_printf (pp
, "%qs", m_name
);
547 deallocator::cmp (const deallocator
*a
, const deallocator
*b
)
549 return (int)a
->m_freed
->get_id () - (int)b
->m_freed
->get_id ();
553 deallocator::cmp_ptr_ptr (const void *a
, const void *b
)
555 return cmp (*(const deallocator
* const *)a
,
556 *(const deallocator
* const *)b
);
560 /* struct standard_deallocator : public deallocator. */
562 standard_deallocator::standard_deallocator (malloc_state_machine
*sm
,
564 enum wording wording
)
565 : deallocator (sm
, name
, wording
)
569 /* struct deallocator_set. */
571 deallocator_set::deallocator_set (malloc_state_machine
*sm
,
572 enum wording wording
)
573 : m_wording (wording
),
574 m_unchecked (sm
->add_state ("unchecked", RS_UNCHECKED
, this, NULL
)),
575 m_nonnull (sm
->add_state ("nonnull", RS_NONNULL
, this, NULL
))
579 /* Dump a description of this deallocator_set to stderr. */
582 deallocator_set::dump () const
585 pp_show_color (&pp
) = pp_show_color (global_dc
->printer
);
586 pp
.buffer
->stream
= stderr
;
592 /* struct custom_deallocator_set : public deallocator_set. */
594 custom_deallocator_set::
595 custom_deallocator_set (malloc_state_machine
*sm
,
596 const auto_vec
<const deallocator
*> *vec
,
597 enum wording wording
)
598 : deallocator_set (sm
, wording
),
599 m_deallocator_vec (vec
->length ())
602 const deallocator
*d
;
603 FOR_EACH_VEC_ELT (*vec
, i
, d
)
604 m_deallocator_vec
.safe_push (d
);
608 custom_deallocator_set::contains_p (const deallocator
*d
) const
611 const deallocator
*cd
;
612 FOR_EACH_VEC_ELT (m_deallocator_vec
, i
, cd
)
619 custom_deallocator_set::maybe_get_single () const
621 if (m_deallocator_vec
.length () == 1)
622 return m_deallocator_vec
[0];
627 custom_deallocator_set::dump_to_pp (pretty_printer
*pp
) const
629 pp_character (pp
, '{');
631 const deallocator
*d
;
632 FOR_EACH_VEC_ELT (m_deallocator_vec
, i
, d
)
635 pp_string (pp
, ", ");
638 pp_character (pp
, '}');
641 /* struct standard_deallocator_set : public deallocator_set. */
643 standard_deallocator_set::standard_deallocator_set (malloc_state_machine
*sm
,
645 enum wording wording
)
646 : deallocator_set (sm
, wording
),
647 m_deallocator (sm
, name
, wording
)
652 standard_deallocator_set::contains_p (const deallocator
*d
) const
654 return d
== &m_deallocator
;
658 standard_deallocator_set::maybe_get_single () const
660 return &m_deallocator
;
664 standard_deallocator_set::dump_to_pp (pretty_printer
*pp
) const
666 pp_character (pp
, '{');
667 pp_string (pp
, m_deallocator
.m_name
);
668 pp_character (pp
, '}');
671 /* Return STATE cast to the custom state subclass, or NULL for the start state.
672 Everything should be an allocation_state apart from the start state. */
674 static const allocation_state
*
675 dyn_cast_allocation_state (state_machine::state_t state
)
677 if (state
->get_id () == 0)
679 return static_cast <const allocation_state
*> (state
);
682 /* Return STATE cast to the custom state subclass, for a state that is
683 already known to not be the start state . */
685 static const allocation_state
*
686 as_a_allocation_state (state_machine::state_t state
)
688 gcc_assert (state
->get_id () != 0);
689 return static_cast <const allocation_state
*> (state
);
692 /* Get the resource_state for STATE. */
694 static enum resource_state
695 get_rs (state_machine::state_t state
)
697 if (const allocation_state
*astate
= dyn_cast_allocation_state (state
))
703 /* Return true if STATE is the start state. */
706 start_p (state_machine::state_t state
)
708 return get_rs (state
) == RS_START
;
711 /* Return true if STATE is an unchecked result from an allocator. */
714 unchecked_p (state_machine::state_t state
)
716 return get_rs (state
) == RS_UNCHECKED
;
719 /* Return true if STATE is a non-null result from an allocator. */
722 nonnull_p (state_machine::state_t state
)
724 return get_rs (state
) == RS_NONNULL
;
727 /* Return true if STATE is a value that has been passed to a deallocator. */
730 freed_p (state_machine::state_t state
)
732 return get_rs (state
) == RS_FREED
;
735 /* Return true if STATE is a value that has been assumed to be non-NULL. */
738 assumed_non_null_p (state_machine::state_t state
)
740 return get_rs (state
) == RS_ASSUMED_NON_NULL
;
743 /* Class for diagnostics relating to malloc_state_machine. */
745 class malloc_diagnostic
: public pending_diagnostic
748 malloc_diagnostic (const malloc_state_machine
&sm
, tree arg
)
749 : m_sm (sm
), m_arg (arg
)
752 bool subclass_equal_p (const pending_diagnostic
&base_other
) const override
754 return same_tree_p (m_arg
, ((const malloc_diagnostic
&)base_other
).m_arg
);
757 label_text
describe_state_change (const evdesc::state_change
&change
)
760 if (change
.m_old_state
== m_sm
.get_start_state ()
761 && (unchecked_p (change
.m_new_state
) || nonnull_p (change
.m_new_state
)))
762 // TODO: verify that it's the allocation stmt, not a copy
763 return label_text::borrow ("allocated here");
764 if (unchecked_p (change
.m_old_state
)
765 && nonnull_p (change
.m_new_state
))
768 return change
.formatted_print ("assuming %qE is non-NULL",
771 return change
.formatted_print ("assuming %qs is non-NULL",
774 if (change
.m_new_state
== m_sm
.m_null
)
776 if (unchecked_p (change
.m_old_state
))
779 return change
.formatted_print ("assuming %qE is NULL",
782 return change
.formatted_print ("assuming %qs is NULL",
788 return change
.formatted_print ("%qE is NULL",
791 return change
.formatted_print ("%qs is NULL",
796 return label_text ();
799 diagnostic_event::meaning
800 get_meaning_for_state_change (const evdesc::state_change
&change
)
803 if (change
.m_old_state
== m_sm
.get_start_state ()
804 && unchecked_p (change
.m_new_state
))
805 return diagnostic_event::meaning (diagnostic_event::VERB_acquire
,
806 diagnostic_event::NOUN_memory
);
807 if (freed_p (change
.m_new_state
))
808 return diagnostic_event::meaning (diagnostic_event::VERB_release
,
809 diagnostic_event::NOUN_memory
);
810 return diagnostic_event::meaning ();
814 const malloc_state_machine
&m_sm
;
818 /* Concrete subclass for reporting mismatching allocator/deallocator
821 class mismatching_deallocation
: public malloc_diagnostic
824 mismatching_deallocation (const malloc_state_machine
&sm
, tree arg
,
825 const deallocator_set
*expected_deallocators
,
826 const deallocator
*actual_dealloc
)
827 : malloc_diagnostic (sm
, arg
),
828 m_expected_deallocators (expected_deallocators
),
829 m_actual_dealloc (actual_dealloc
)
832 const char *get_kind () const final override
834 return "mismatching_deallocation";
837 int get_controlling_option () const final override
839 return OPT_Wanalyzer_mismatching_deallocation
;
842 bool emit (diagnostic_emission_context
&ctxt
) final override
844 auto_diagnostic_group d
;
845 ctxt
.add_cwe (762); /* CWE-762: Mismatched Memory Management Routines. */
846 if (const deallocator
*expected_dealloc
847 = m_expected_deallocators
->maybe_get_single ())
848 return ctxt
.warn ("%qE should have been deallocated with %qs"
849 " but was deallocated with %qs",
850 m_arg
, expected_dealloc
->m_name
,
851 m_actual_dealloc
->m_name
);
853 return ctxt
.warn ("%qs called on %qE returned from a mismatched"
854 " allocation function",
855 m_actual_dealloc
->m_name
, m_arg
);
858 label_text
describe_state_change (const evdesc::state_change
&change
)
861 if (unchecked_p (change
.m_new_state
))
863 m_alloc_event
= change
.m_event_id
;
864 if (const deallocator
*expected_dealloc
865 = m_expected_deallocators
->maybe_get_single ())
866 return change
.formatted_print ("allocated here"
867 " (expects deallocation with %qs)",
868 expected_dealloc
->m_name
);
870 return change
.formatted_print ("allocated here");
872 return malloc_diagnostic::describe_state_change (change
);
875 label_text
describe_final_event (const evdesc::final_event
&ev
) final override
877 if (m_alloc_event
.known_p ())
879 if (const deallocator
*expected_dealloc
880 = m_expected_deallocators
->maybe_get_single ())
881 return ev
.formatted_print
882 ("deallocated with %qs here;"
883 " allocation at %@ expects deallocation with %qs",
884 m_actual_dealloc
->m_name
, &m_alloc_event
,
885 expected_dealloc
->m_name
);
887 return ev
.formatted_print
888 ("deallocated with %qs here;"
890 m_actual_dealloc
->m_name
, &m_alloc_event
);
892 return ev
.formatted_print ("deallocated with %qs here",
893 m_actual_dealloc
->m_name
);
897 diagnostic_event_id_t m_alloc_event
;
898 const deallocator_set
*m_expected_deallocators
;
899 const deallocator
*m_actual_dealloc
;
902 /* Concrete subclass for reporting double-free diagnostics. */
904 class double_free
: public malloc_diagnostic
907 double_free (const malloc_state_machine
&sm
, tree arg
, const char *funcname
)
908 : malloc_diagnostic (sm
, arg
), m_funcname (funcname
)
911 const char *get_kind () const final override
{ return "double_free"; }
913 int get_controlling_option () const final override
915 return OPT_Wanalyzer_double_free
;
918 bool emit (diagnostic_emission_context
&ctxt
) final override
920 auto_diagnostic_group d
;
921 ctxt
.add_cwe (415); /* CWE-415: Double Free. */
922 return ctxt
.warn ("double-%qs of %qE", m_funcname
, m_arg
);
925 label_text
describe_state_change (const evdesc::state_change
&change
)
928 if (freed_p (change
.m_new_state
))
930 m_first_free_event
= change
.m_event_id
;
931 return change
.formatted_print ("first %qs here", m_funcname
);
933 return malloc_diagnostic::describe_state_change (change
);
936 label_text
describe_call_with_state (const evdesc::call_with_state
&info
)
939 if (freed_p (info
.m_state
))
940 return info
.formatted_print
941 ("passing freed pointer %qE in call to %qE from %qE",
942 info
.m_expr
, info
.m_callee_fndecl
, info
.m_caller_fndecl
);
943 return label_text ();
946 label_text
describe_final_event (const evdesc::final_event
&ev
) final override
948 if (m_first_free_event
.known_p ())
949 return ev
.formatted_print ("second %qs here; first %qs was at %@",
950 m_funcname
, m_funcname
,
951 &m_first_free_event
);
952 return ev
.formatted_print ("second %qs here", m_funcname
);
956 diagnostic_event_id_t m_first_free_event
;
957 const char *m_funcname
;
960 /* Abstract subclass for describing possible bad uses of NULL.
961 Responsible for describing the call that could return NULL. */
963 class possible_null
: public malloc_diagnostic
966 possible_null (const malloc_state_machine
&sm
, tree arg
)
967 : malloc_diagnostic (sm
, arg
)
970 label_text
describe_state_change (const evdesc::state_change
&change
)
973 if (change
.m_old_state
== m_sm
.get_start_state ()
974 && unchecked_p (change
.m_new_state
))
976 m_origin_of_unchecked_event
= change
.m_event_id
;
977 return label_text::borrow ("this call could return NULL");
979 return malloc_diagnostic::describe_state_change (change
);
982 label_text
describe_return_of_state (const evdesc::return_of_state
&info
)
985 if (unchecked_p (info
.m_state
))
986 return info
.formatted_print ("possible return of NULL to %qE from %qE",
987 info
.m_caller_fndecl
, info
.m_callee_fndecl
);
988 return label_text ();
992 diagnostic_event_id_t m_origin_of_unchecked_event
;
995 /* Concrete subclass for describing dereference of a possible NULL
998 class possible_null_deref
: public possible_null
1001 possible_null_deref (const malloc_state_machine
&sm
, tree arg
)
1002 : possible_null (sm
, arg
)
1005 const char *get_kind () const final override
{ return "possible_null_deref"; }
1007 int get_controlling_option () const final override
1009 return OPT_Wanalyzer_possible_null_dereference
;
1012 bool emit (diagnostic_emission_context
&ctxt
) final override
1014 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
1016 return ctxt
.warn ("dereference of possibly-NULL %qE", m_arg
);
1019 label_text
describe_final_event (const evdesc::final_event
&ev
) final override
1021 if (m_origin_of_unchecked_event
.known_p ())
1022 return ev
.formatted_print ("%qE could be NULL: unchecked value from %@",
1024 &m_origin_of_unchecked_event
);
1026 return ev
.formatted_print ("%qE could be NULL", ev
.m_expr
);
1031 /* Return true if FNDECL is a C++ method. */
1034 method_p (tree fndecl
)
1036 return TREE_CODE (TREE_TYPE (fndecl
)) == METHOD_TYPE
;
1039 /* Return a 1-based description of ARG_IDX (0-based) of FNDECL.
1040 Compare with %P in the C++ FE (implemented in cp/error.cc: parm_to_string
1041 as called from cp_printer). */
1044 describe_argument_index (tree fndecl
, int arg_idx
)
1046 if (method_p (fndecl
))
1048 return label_text::borrow ("'this'");
1050 pp_printf (&pp
, "%u", arg_idx
+ 1 - method_p (fndecl
));
1051 return label_text::take (xstrdup (pp_formatted_text (&pp
)));
1054 /* Subroutine for use by possible_null_arg::emit and null_arg::emit.
1055 Issue a note informing that the pertinent argument must be non-NULL. */
1058 inform_nonnull_attribute (tree fndecl
, int arg_idx
)
1060 label_text arg_desc
= describe_argument_index (fndecl
, arg_idx
);
1061 inform (DECL_SOURCE_LOCATION (fndecl
),
1062 "argument %s of %qD must be non-null",
1063 arg_desc
.get (), fndecl
);
1064 /* Ideally we would use the location of the parm and underline the
1065 attribute also - but we don't have the location_t values at this point
1067 For reference, the C and C++ FEs have get_fndecl_argument_location. */
1070 /* Concrete subclass for describing passing a possibly-NULL value to a
1071 function marked with __attribute__((nonnull)). */
1073 class possible_null_arg
: public possible_null
1076 possible_null_arg (const malloc_state_machine
&sm
, tree arg
,
1077 tree fndecl
, int arg_idx
)
1078 : possible_null (sm
, arg
),
1079 m_fndecl (fndecl
), m_arg_idx (arg_idx
)
1082 const char *get_kind () const final override
{ return "possible_null_arg"; }
1084 bool subclass_equal_p (const pending_diagnostic
&base_other
)
1085 const final override
1087 const possible_null_arg
&sub_other
1088 = (const possible_null_arg
&)base_other
;
1089 return (same_tree_p (m_arg
, sub_other
.m_arg
)
1090 && m_fndecl
== sub_other
.m_fndecl
1091 && m_arg_idx
== sub_other
.m_arg_idx
);
1094 int get_controlling_option () const final override
1096 return OPT_Wanalyzer_possible_null_argument
;
1099 bool emit (diagnostic_emission_context
&ctxt
) final override
1101 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
1102 auto_diagnostic_group d
;
1105 = ctxt
.warn ("use of possibly-NULL %qE where non-null expected",
1108 inform_nonnull_attribute (m_fndecl
, m_arg_idx
);
1112 label_text
describe_final_event (const evdesc::final_event
&ev
) final override
1114 label_text arg_desc
= describe_argument_index (m_fndecl
, m_arg_idx
);
1116 if (m_origin_of_unchecked_event
.known_p ())
1117 result
= ev
.formatted_print ("argument %s (%qE) from %@ could be NULL"
1118 " where non-null expected",
1119 arg_desc
.get (), ev
.m_expr
,
1120 &m_origin_of_unchecked_event
);
1122 result
= ev
.formatted_print ("argument %s (%qE) could be NULL"
1123 " where non-null expected",
1124 arg_desc
.get (), ev
.m_expr
);
1133 /* Concrete subclass for describing a dereference of a NULL value. */
1135 class null_deref
: public malloc_diagnostic
1138 null_deref (const malloc_state_machine
&sm
, tree arg
)
1139 : malloc_diagnostic (sm
, arg
) {}
1141 const char *get_kind () const final override
{ return "null_deref"; }
1143 int get_controlling_option () const final override
1145 return OPT_Wanalyzer_null_dereference
;
1148 bool terminate_path_p () const final override
{ return true; }
1150 bool emit (diagnostic_emission_context
&ctxt
) final override
1152 /* CWE-476: NULL Pointer Dereference. */
1154 return ctxt
.warn ("dereference of NULL %qE", m_arg
);
1157 label_text
describe_return_of_state (const evdesc::return_of_state
&info
)
1160 if (info
.m_state
== m_sm
.m_null
)
1161 return info
.formatted_print ("return of NULL to %qE from %qE",
1162 info
.m_caller_fndecl
, info
.m_callee_fndecl
);
1163 return label_text ();
1166 label_text
describe_final_event (const evdesc::final_event
&ev
) final override
1168 return ev
.formatted_print ("dereference of NULL %qE", ev
.m_expr
);
1171 /* Implementation of pending_diagnostic::supercedes_p for
1174 We want null-deref to supercede use-of-unitialized-value,
1175 so that if we have these at the same stmt, we don't emit
1176 a use-of-uninitialized, just the null-deref. */
1178 bool supercedes_p (const pending_diagnostic
&other
) const final override
1180 if (other
.use_of_uninit_p ())
1187 /* Concrete subclass for describing passing a NULL value to a
1188 function marked with __attribute__((nonnull)). */
1190 class null_arg
: public malloc_diagnostic
1193 null_arg (const malloc_state_machine
&sm
, tree arg
,
1194 tree fndecl
, int arg_idx
)
1195 : malloc_diagnostic (sm
, arg
),
1196 m_fndecl (fndecl
), m_arg_idx (arg_idx
)
1199 const char *get_kind () const final override
{ return "null_arg"; }
1201 bool subclass_equal_p (const pending_diagnostic
&base_other
)
1202 const final override
1204 const null_arg
&sub_other
1205 = (const null_arg
&)base_other
;
1206 return (same_tree_p (m_arg
, sub_other
.m_arg
)
1207 && m_fndecl
== sub_other
.m_fndecl
1208 && m_arg_idx
== sub_other
.m_arg_idx
);
1211 int get_controlling_option () const final override
1213 return OPT_Wanalyzer_null_argument
;
1216 bool terminate_path_p () const final override
{ return true; }
1218 bool emit (diagnostic_emission_context
&ctxt
) final override
1220 /* CWE-476: NULL Pointer Dereference. */
1221 auto_diagnostic_group d
;
1226 warned
= ctxt
.warn ("use of NULL where non-null expected");
1228 warned
= ctxt
.warn ("use of NULL %qE where non-null expected",
1231 inform_nonnull_attribute (m_fndecl
, m_arg_idx
);
1235 label_text
describe_final_event (const evdesc::final_event
&ev
) final override
1237 label_text arg_desc
= describe_argument_index (m_fndecl
, m_arg_idx
);
1239 if (zerop (ev
.m_expr
))
1240 result
= ev
.formatted_print ("argument %s NULL where non-null expected",
1243 result
= ev
.formatted_print ("argument %s (%qE) NULL"
1244 " where non-null expected",
1245 arg_desc
.get (), ev
.m_expr
);
1254 class use_after_free
: public malloc_diagnostic
1257 use_after_free (const malloc_state_machine
&sm
, tree arg
,
1258 const deallocator
*deallocator
)
1259 : malloc_diagnostic (sm
, arg
),
1260 m_deallocator (deallocator
)
1262 gcc_assert (deallocator
);
1265 const char *get_kind () const final override
{ return "use_after_free"; }
1267 int get_controlling_option () const final override
1269 return OPT_Wanalyzer_use_after_free
;
1272 bool emit (diagnostic_emission_context
&ctxt
) final override
1274 /* CWE-416: Use After Free. */
1276 return ctxt
.warn ("use after %<%s%> of %qE",
1277 m_deallocator
->m_name
, m_arg
);
1280 label_text
describe_state_change (const evdesc::state_change
&change
)
1283 if (freed_p (change
.m_new_state
))
1285 m_free_event
= change
.m_event_id
;
1286 switch (m_deallocator
->m_wording
)
1289 case WORDING_REALLOCATED
:
1292 return label_text::borrow ("freed here");
1293 case WORDING_DELETED
:
1294 return label_text::borrow ("deleted here");
1295 case WORDING_DEALLOCATED
:
1296 return label_text::borrow ("deallocated here");
1299 return malloc_diagnostic::describe_state_change (change
);
1302 label_text
describe_final_event (const evdesc::final_event
&ev
) final override
1304 const char *funcname
= m_deallocator
->m_name
;
1305 if (m_free_event
.known_p ())
1306 switch (m_deallocator
->m_wording
)
1309 case WORDING_REALLOCATED
:
1312 return ev
.formatted_print ("use after %<%s%> of %qE; freed at %@",
1313 funcname
, ev
.m_expr
, &m_free_event
);
1314 case WORDING_DELETED
:
1315 return ev
.formatted_print ("use after %<%s%> of %qE; deleted at %@",
1316 funcname
, ev
.m_expr
, &m_free_event
);
1317 case WORDING_DEALLOCATED
:
1318 return ev
.formatted_print ("use after %<%s%> of %qE;"
1319 " deallocated at %@",
1320 funcname
, ev
.m_expr
, &m_free_event
);
1323 return ev
.formatted_print ("use after %<%s%> of %qE",
1324 funcname
, ev
.m_expr
);
1327 /* Implementation of pending_diagnostic::supercedes_p for
1330 We want use-after-free to supercede use-of-unitialized-value,
1331 so that if we have these at the same stmt, we don't emit
1332 a use-of-uninitialized, just the use-after-free.
1333 (this is because we fully purge information about freed
1334 buffers when we free them to avoid state explosions, so
1335 that if they are accessed after the free, it looks like
1336 they are uninitialized). */
1338 bool supercedes_p (const pending_diagnostic
&other
) const final override
1340 if (other
.use_of_uninit_p ())
1347 diagnostic_event_id_t m_free_event
;
1348 const deallocator
*m_deallocator
;
1351 class malloc_leak
: public malloc_diagnostic
1354 malloc_leak (const malloc_state_machine
&sm
, tree arg
)
1355 : malloc_diagnostic (sm
, arg
) {}
1357 const char *get_kind () const final override
{ return "malloc_leak"; }
1359 int get_controlling_option () const final override
1361 return OPT_Wanalyzer_malloc_leak
;
1364 bool emit (diagnostic_emission_context
&ctxt
) final override
1366 /* "CWE-401: Missing Release of Memory after Effective Lifetime". */
1369 return ctxt
.warn ("leak of %qE", m_arg
);
1371 return ctxt
.warn ("leak of %qs", "<unknown>");
1374 label_text
describe_state_change (const evdesc::state_change
&change
)
1377 if (unchecked_p (change
.m_new_state
)
1378 || (start_p (change
.m_old_state
) && nonnull_p (change
.m_new_state
)))
1380 m_alloc_event
= change
.m_event_id
;
1381 return label_text::borrow ("allocated here");
1383 return malloc_diagnostic::describe_state_change (change
);
1386 label_text
describe_final_event (const evdesc::final_event
&ev
) final override
1390 if (m_alloc_event
.known_p ())
1391 return ev
.formatted_print ("%qE leaks here; was allocated at %@",
1392 ev
.m_expr
, &m_alloc_event
);
1394 return ev
.formatted_print ("%qE leaks here", ev
.m_expr
);
1398 if (m_alloc_event
.known_p ())
1399 return ev
.formatted_print ("%qs leaks here; was allocated at %@",
1400 "<unknown>", &m_alloc_event
);
1402 return ev
.formatted_print ("%qs leaks here", "<unknown>");
1407 diagnostic_event_id_t m_alloc_event
;
1410 class free_of_non_heap
: public malloc_diagnostic
1413 free_of_non_heap (const malloc_state_machine
&sm
, tree arg
,
1414 const region
*freed_reg
,
1415 const char *funcname
)
1416 : malloc_diagnostic (sm
, arg
), m_freed_reg (freed_reg
), m_funcname (funcname
)
1420 const char *get_kind () const final override
{ return "free_of_non_heap"; }
1422 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
1425 const free_of_non_heap
&other
= (const free_of_non_heap
&)base_other
;
1426 return (same_tree_p (m_arg
, other
.m_arg
)
1427 && m_freed_reg
== other
.m_freed_reg
);
1430 int get_controlling_option () const final override
1432 return OPT_Wanalyzer_free_of_non_heap
;
1435 bool emit (diagnostic_emission_context
&ctxt
) final override
1437 auto_diagnostic_group d
;
1438 ctxt
.add_cwe (590); /* CWE-590: Free of Memory not on the Heap. */
1439 switch (get_memory_space ())
1444 case MEMSPACE_UNKNOWN
:
1446 case MEMSPACE_GLOBALS
:
1447 case MEMSPACE_READONLY_DATA
:
1448 return ctxt
.warn ("%<%s%> of %qE which points to memory"
1452 case MEMSPACE_STACK
:
1453 return ctxt
.warn ("%<%s%> of %qE which points to memory"
1460 label_text
describe_state_change (const evdesc::state_change
&)
1463 return label_text::borrow ("pointer is from here");
1466 label_text
describe_final_event (const evdesc::final_event
&ev
) final override
1468 return ev
.formatted_print ("call to %qs here", m_funcname
);
1471 void mark_interesting_stuff (interesting_t
*interest
) final override
1474 interest
->add_region_creation (m_freed_reg
);
1478 enum memory_space
get_memory_space () const
1481 return m_freed_reg
->get_memory_space ();
1483 return MEMSPACE_UNKNOWN
;
1486 const region
*m_freed_reg
;
1487 const char *m_funcname
;
1490 /* Concrete pending_diagnostic subclass for -Wanalyzer-deref-before-check. */
1492 class deref_before_check
: public malloc_diagnostic
1495 deref_before_check (const malloc_state_machine
&sm
, tree arg
)
1496 : malloc_diagnostic (sm
, arg
),
1497 m_deref_enode (NULL
),
1498 m_deref_expr (NULL
),
1499 m_check_enode (NULL
)
1504 const char *get_kind () const final override
{ return "deref_before_check"; }
1506 int get_controlling_option () const final override
1508 return OPT_Wanalyzer_deref_before_check
;
1511 bool emit (diagnostic_emission_context
&ctxt
) final override
1513 /* Don't emit the warning if we can't show where the deref
1514 and the check occur. */
1519 /* Only emit the warning for intraprocedural cases. */
1520 const program_point
&deref_point
= m_deref_enode
->get_point ();
1521 const program_point
&check_point
= m_check_enode
->get_point ();
1523 if (!program_point::effectively_intraprocedural_p (deref_point
,
1527 /* Reject the warning if the check occurs within a macro defintion.
1528 This avoids false positives for such code as:
1530 #define throw_error \
1540 where the usage of "throw_error" implicitly adds a check
1543 We do warn when the check is in a macro expansion if we can get
1544 at the location of the condition and it is't part of the
1545 definition, so that we warn for checks such as:
1546 if (words[0][0] == '@')
1548 g_assert(words[0] != NULL); <--- here
1549 Unfortunately we don't have locations for individual gimple
1552 we merely have a gimple_cond
1554 with no way of getting at the location of the condition separately
1555 from that of the gimple_cond (where the "if" is within the macro
1556 definition). We reject the warning for such cases.
1558 We do warn when the *deref* occurs in a macro, since this can be
1559 a source of real bugs; see e.g. PR 77425. */
1560 location_t check_loc
= m_check_enode
->get_point ().get_location ();
1561 if (linemap_location_from_macro_definition_p (line_table
, check_loc
))
1564 /* Reject if m_deref_expr is sufficiently different from m_arg
1565 for cases where the dereference is spelled differently from
1566 the check, which is probably two different ways to get the
1567 same svalue, and thus not worth reporting. */
1570 if (!sufficiently_similar_p (m_deref_expr
, m_arg
))
1573 /* Reject the warning if the deref's BB doesn't dominate that
1574 of the check, so that we don't warn e.g. for shared cleanup
1575 code that checks a pointer for NULL, when that code is sometimes
1576 used before a deref and sometimes after.
1577 Using the dominance code requires setting cfun. */
1578 auto_cfun
sentinel (m_deref_enode
->get_function ());
1579 calculate_dominance_info (CDI_DOMINATORS
);
1580 if (!dominated_by_p (CDI_DOMINATORS
,
1581 m_check_enode
->get_supernode ()->m_bb
,
1582 m_deref_enode
->get_supernode ()->m_bb
))
1585 return ctxt
.warn ("check of %qE for NULL after already"
1586 " dereferencing it",
1590 label_text
describe_state_change (const evdesc::state_change
&change
)
1593 if (change
.m_old_state
== m_sm
.get_start_state ()
1594 && assumed_non_null_p (change
.m_new_state
))
1596 m_first_deref_event
= change
.m_event_id
;
1597 m_deref_enode
= change
.m_event
.get_exploded_node ();
1598 m_deref_expr
= change
.m_expr
;
1599 return change
.formatted_print ("pointer %qE is dereferenced here",
1602 return malloc_diagnostic::describe_state_change (change
);
1605 label_text
describe_final_event (const evdesc::final_event
&ev
) final override
1607 m_check_enode
= ev
.m_event
.get_exploded_node ();
1608 if (m_first_deref_event
.known_p ())
1609 return ev
.formatted_print ("pointer %qE is checked for NULL here but"
1610 " it was already dereferenced at %@",
1611 m_arg
, &m_first_deref_event
);
1613 return ev
.formatted_print ("pointer %qE is checked for NULL here but"
1614 " it was already dereferenced",
1619 static bool sufficiently_similar_p (tree expr_a
, tree expr_b
)
1621 pretty_printer
*pp_a
= global_dc
->printer
->clone ();
1622 pretty_printer
*pp_b
= global_dc
->printer
->clone ();
1623 pp_printf (pp_a
, "%qE", expr_a
);
1624 pp_printf (pp_b
, "%qE", expr_b
);
1625 bool result
= (strcmp (pp_formatted_text (pp_a
), pp_formatted_text (pp_b
))
1632 diagnostic_event_id_t m_first_deref_event
;
1633 const exploded_node
*m_deref_enode
;
1635 const exploded_node
*m_check_enode
;
1638 /* struct allocation_state : public state_machine::state. */
1640 /* Implementation of state_machine::state::dump_to_pp vfunc
1641 for allocation_state: append the API that this allocation is
1645 allocation_state::dump_to_pp (pretty_printer
*pp
) const
1647 state_machine::state::dump_to_pp (pp
);
1650 pp_string (pp
, " (");
1651 m_deallocators
->dump_to_pp (pp
);
1652 pp_character (pp
, ')');
1656 /* Given a allocation_state for a deallocator_set, get the "nonnull" state
1657 for the corresponding allocator(s). */
1659 const allocation_state
*
1660 allocation_state::get_nonnull () const
1662 gcc_assert (m_deallocators
);
1663 return as_a_allocation_state (m_deallocators
->m_nonnull
);
1666 /* struct assumed_non_null_state : public allocation_state. */
1669 assumed_non_null_state::dump_to_pp (pretty_printer
*pp
) const
1671 allocation_state::dump_to_pp (pp
);
1672 pp_string (pp
, " (in ");
1673 m_frame
->dump_to_pp (pp
, true);
1674 pp_character (pp
, ')');
1677 /* malloc_state_machine's ctor. */
1679 malloc_state_machine::malloc_state_machine (logger
*logger
)
1680 : state_machine ("malloc", logger
),
1681 m_free (this, "free", WORDING_FREED
),
1682 m_scalar_delete (this, "delete", WORDING_DELETED
),
1683 m_vector_delete (this, "delete[]", WORDING_DELETED
),
1684 m_realloc (this, "realloc", WORDING_REALLOCATED
)
1686 gcc_assert (m_start
->get_id () == 0);
1687 m_null
= add_state ("null", RS_FREED
, NULL
, NULL
);
1688 m_non_heap
= add_state ("non-heap", RS_NON_HEAP
, NULL
, NULL
);
1689 m_stop
= add_state ("stop", RS_STOP
, NULL
, NULL
);
1692 malloc_state_machine::~malloc_state_machine ()
1695 custom_deallocator_set
*set
;
1696 FOR_EACH_VEC_ELT (m_dynamic_sets
, i
, set
)
1698 custom_deallocator
*d
;
1699 FOR_EACH_VEC_ELT (m_dynamic_deallocators
, i
, d
)
1703 state_machine::state_t
1704 malloc_state_machine::add_state (const char *name
, enum resource_state rs
,
1705 const deallocator_set
*deallocators
,
1706 const deallocator
*deallocator
)
1708 return add_custom_state (new allocation_state (name
, alloc_state_id (),
1713 /* If ALLOCATOR_FNDECL has any "__attribute__((malloc(FOO)))",
1714 return a custom_deallocator_set for them, consolidating them
1715 to ensure uniqueness of the sets.
1717 Return NULL if it has no such attributes. */
1719 const custom_deallocator_set
*
1720 malloc_state_machine::
1721 get_or_create_custom_deallocator_set (tree allocator_fndecl
)
1723 /* Early rejection of decls without attributes. */
1724 tree attrs
= DECL_ATTRIBUTES (allocator_fndecl
);
1728 /* Otherwise, call maybe_create_custom_deallocator_set,
1729 memoizing the result. */
1730 if (custom_deallocator_set
**slot
1731 = m_custom_deallocator_set_cache
.get (allocator_fndecl
))
1733 custom_deallocator_set
*set
1734 = maybe_create_custom_deallocator_set (allocator_fndecl
);
1735 m_custom_deallocator_set_cache
.put (allocator_fndecl
, set
);
1739 /* Given ALLOCATOR_FNDECL, a FUNCTION_DECL with attributes,
1740 look for any "__attribute__((malloc(FOO)))" and return a
1741 custom_deallocator_set for them, consolidating them
1742 to ensure uniqueness of the sets.
1744 Return NULL if it has no such attributes.
1746 Subroutine of get_or_create_custom_deallocator_set which
1747 memoizes the result. */
1749 custom_deallocator_set
*
1750 malloc_state_machine::
1751 maybe_create_custom_deallocator_set (tree allocator_fndecl
)
1753 tree attrs
= DECL_ATTRIBUTES (allocator_fndecl
);
1756 /* Look for instances of __attribute__((malloc(FOO))). */
1757 auto_vec
<const deallocator
*> deallocator_vec
;
1758 for (tree allocs
= attrs
;
1759 (allocs
= lookup_attribute ("malloc", allocs
));
1760 allocs
= TREE_CHAIN (allocs
))
1762 tree args
= TREE_VALUE (allocs
);
1765 if (TREE_VALUE (args
))
1767 const deallocator
*d
1768 = get_or_create_deallocator (TREE_VALUE (args
));
1769 deallocator_vec
.safe_push (d
);
1773 /* If there weren't any deallocators, bail. */
1774 if (deallocator_vec
.length () == 0)
1777 /* Consolidate, so that we reuse existing deallocator_set
1779 deallocator_vec
.qsort (deallocator::cmp_ptr_ptr
);
1780 custom_deallocator_set
**slot
1781 = m_custom_deallocator_set_map
.get (&deallocator_vec
);
1784 custom_deallocator_set
*set
1785 = new custom_deallocator_set (this, &deallocator_vec
, WORDING_DEALLOCATED
);
1786 m_custom_deallocator_set_map
.put (&set
->m_deallocator_vec
, set
);
1787 m_dynamic_sets
.safe_push (set
);
1791 /* Get the deallocator for DEALLOCATOR_FNDECL, creating it if necessary. */
1794 malloc_state_machine::get_or_create_deallocator (tree deallocator_fndecl
)
1796 deallocator
**slot
= m_deallocator_map
.get (deallocator_fndecl
);
1802 if (is_named_call_p (deallocator_fndecl
, "free")
1803 || is_std_named_call_p (deallocator_fndecl
, "free")
1804 || is_named_call_p (deallocator_fndecl
, "__builtin_free"))
1805 d
= &m_free
.m_deallocator
;
1808 custom_deallocator
*cd
1809 = new custom_deallocator (this, deallocator_fndecl
,
1810 WORDING_DEALLOCATED
);
1811 m_dynamic_deallocators
.safe_push (cd
);
1814 m_deallocator_map
.put (deallocator_fndecl
, d
);
1818 /* Get the "assumed-non-null" state for assumptions made within FRAME,
1819 creating it if necessary. */
1821 state_machine::state_t
1822 malloc_state_machine::
1823 get_or_create_assumed_non_null_state_for_frame (const frame_region
*frame
)
1825 if (state_t
*slot
= m_assumed_non_null
.get (frame
))
1827 state_machine::state
*new_state
1828 = new assumed_non_null_state ("assumed-non-null", alloc_state_id (), frame
);
1829 add_custom_state (new_state
);
1830 m_assumed_non_null
.put (frame
, new_state
);
1834 /* Try to identify the function declaration either by name or as a known malloc
1838 known_allocator_p (const_tree fndecl
, const gcall
*call
)
1840 /* Either it is a function we know by name and number of arguments... */
1841 if (is_named_call_p (fndecl
, "malloc", call
, 1)
1842 || is_named_call_p (fndecl
, "calloc", call
, 2)
1843 || is_std_named_call_p (fndecl
, "malloc", call
, 1)
1844 || is_std_named_call_p (fndecl
, "calloc", call
, 2)
1845 || is_named_call_p (fndecl
, "strdup", call
, 1)
1846 || is_named_call_p (fndecl
, "strndup", call
, 2))
1849 /* ... or it is a builtin allocator that allocates objects freed with
1851 if (fndecl_built_in_p (fndecl
, BUILT_IN_NORMAL
))
1852 switch (DECL_FUNCTION_CODE (fndecl
))
1854 case BUILT_IN_MALLOC
:
1855 case BUILT_IN_CALLOC
:
1856 case BUILT_IN_STRDUP
:
1857 case BUILT_IN_STRNDUP
:
1866 /* If PTR's nullness is not known, transition it to the "assumed-non-null"
1867 state for the current frame. */
1870 malloc_state_machine::maybe_assume_non_null (sm_context
*sm_ctxt
,
1872 const gimple
*stmt
) const
1874 const region_model
*old_model
= sm_ctxt
->get_old_region_model ();
1878 tree null_ptr_cst
= build_int_cst (TREE_TYPE (ptr
), 0);
1879 tristate known_non_null
1880 = old_model
->eval_condition (ptr
, NE_EXPR
, null_ptr_cst
, NULL
);
1881 if (known_non_null
.is_unknown ())
1883 /* Cast away const-ness for cache-like operations. */
1884 malloc_state_machine
*mut_this
1885 = const_cast <malloc_state_machine
*> (this);
1887 = mut_this
->get_or_create_assumed_non_null_state_for_frame
1888 (old_model
->get_current_frame ());
1889 sm_ctxt
->set_next_state (stmt
, ptr
, next_state
);
1893 /* Implementation of state_machine::on_stmt vfunc for malloc_state_machine. */
1896 malloc_state_machine::on_stmt (sm_context
*sm_ctxt
,
1897 const supernode
*node
,
1898 const gimple
*stmt
) const
1900 if (const gcall
*call
= dyn_cast
<const gcall
*> (stmt
))
1901 if (tree callee_fndecl
= sm_ctxt
->get_fndecl_for_call (call
))
1903 if (known_allocator_p (callee_fndecl
, call
))
1905 on_allocator_call (sm_ctxt
, call
, &m_free
);
1909 if (!is_placement_new_p (call
))
1911 bool returns_nonnull
= !TREE_NOTHROW (callee_fndecl
)
1913 if (is_named_call_p (callee_fndecl
, "operator new"))
1914 on_allocator_call (sm_ctxt
, call
,
1915 &m_scalar_delete
, returns_nonnull
);
1916 else if (is_named_call_p (callee_fndecl
, "operator new []"))
1917 on_allocator_call (sm_ctxt
, call
,
1918 &m_vector_delete
, returns_nonnull
);
1921 if (is_named_call_p (callee_fndecl
, "operator delete", call
, 1)
1922 || is_named_call_p (callee_fndecl
, "operator delete", call
, 2))
1924 on_deallocator_call (sm_ctxt
, node
, call
,
1925 &m_scalar_delete
.m_deallocator
, 0);
1928 else if (is_named_call_p (callee_fndecl
, "operator delete []", call
, 1))
1930 on_deallocator_call (sm_ctxt
, node
, call
,
1931 &m_vector_delete
.m_deallocator
, 0);
1935 if (is_named_call_p (callee_fndecl
, "alloca", call
, 1)
1936 || is_named_call_p (callee_fndecl
, "__builtin_alloca", call
, 1))
1938 tree lhs
= gimple_call_lhs (call
);
1940 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
, m_non_heap
);
1944 if (is_named_call_p (callee_fndecl
, "free", call
, 1)
1945 || is_std_named_call_p (callee_fndecl
, "free", call
, 1)
1946 || is_named_call_p (callee_fndecl
, "__builtin_free", call
, 1))
1948 on_deallocator_call (sm_ctxt
, node
, call
,
1949 &m_free
.m_deallocator
, 0);
1953 if (is_named_call_p (callee_fndecl
, "realloc", call
, 2)
1954 || is_named_call_p (callee_fndecl
, "__builtin_realloc", call
, 2))
1956 on_realloc_call (sm_ctxt
, node
, call
);
1960 if (unaffected_by_call_p (callee_fndecl
))
1963 /* Cast away const-ness for cache-like operations. */
1964 malloc_state_machine
*mutable_this
1965 = const_cast <malloc_state_machine
*> (this);
1967 /* Handle interesting attributes of the callee_fndecl,
1968 or prioritize those of the builtin that callee_fndecl is expected
1970 Might want this to be controlled by a flag. */
1972 tree fndecl
= callee_fndecl
;
1973 /* If call is recognized as a builtin known_function, use that
1974 builtin's function_decl. */
1975 if (const region_model
*old_model
= sm_ctxt
->get_old_region_model ())
1976 if (const builtin_known_function
*builtin_kf
1977 = old_model
->get_builtin_kf (call
))
1978 fndecl
= builtin_kf
->builtin_decl ();
1980 /* Handle "__attribute__((malloc(FOO)))". */
1981 if (const deallocator_set
*deallocators
1982 = mutable_this
->get_or_create_custom_deallocator_set
1985 tree attrs
= TYPE_ATTRIBUTES (TREE_TYPE (fndecl
));
1986 bool returns_nonnull
1987 = lookup_attribute ("returns_nonnull", attrs
);
1988 on_allocator_call (sm_ctxt
, call
, deallocators
, returns_nonnull
);
1992 /* Handle "__attribute__((nonnull))". */
1993 tree fntype
= TREE_TYPE (fndecl
);
1994 bitmap nonnull_args
= get_nonnull_args (fntype
);
1997 for (unsigned i
= 0; i
< gimple_call_num_args (stmt
); i
++)
1999 tree arg
= gimple_call_arg (stmt
, i
);
2000 if (TREE_CODE (TREE_TYPE (arg
)) != POINTER_TYPE
)
2002 /* If we have a nonnull-args, and either all pointers, or
2003 just the specified pointers. */
2004 if (bitmap_empty_p (nonnull_args
)
2005 || bitmap_bit_p (nonnull_args
, i
))
2007 state_t state
= sm_ctxt
->get_state (stmt
, arg
);
2008 /* Can't use a switch as the states are non-const. */
2009 /* Do use the fndecl that caused the warning so that the
2010 misused attributes are printed and the user not
2012 if (unchecked_p (state
))
2014 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
2015 sm_ctxt
->warn (node
, stmt
, arg
,
2016 make_unique
<possible_null_arg
>
2017 (*this, diag_arg
, fndecl
, i
));
2018 const allocation_state
*astate
2019 = as_a_allocation_state (state
);
2020 sm_ctxt
->set_next_state (stmt
, arg
,
2021 astate
->get_nonnull ());
2023 else if (state
== m_null
)
2025 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
2026 sm_ctxt
->warn (node
, stmt
, arg
,
2027 make_unique
<null_arg
>
2028 (*this, diag_arg
, fndecl
, i
));
2029 sm_ctxt
->set_next_state (stmt
, arg
, m_stop
);
2031 else if (state
== m_start
)
2032 maybe_assume_non_null (sm_ctxt
, arg
, stmt
);
2035 BITMAP_FREE (nonnull_args
);
2039 /* Check for this after nonnull, so that if we have both
2040 then we transition to "freed", rather than "checked". */
2041 unsigned dealloc_argno
= fndecl_dealloc_argno (fndecl
);
2042 if (dealloc_argno
!= UINT_MAX
)
2044 const deallocator
*d
2045 = mutable_this
->get_or_create_deallocator (fndecl
);
2046 on_deallocator_call (sm_ctxt
, node
, call
, d
, dealloc_argno
);
2051 /* Look for pointers explicitly being compared against zero
2052 that are in state assumed_non_null i.e. we already defererenced
2054 We have to do this check here, rather than in on_condition
2055 because we add a constraint that the pointer is non-null when
2056 dereferencing it, and this makes the apply_constraints_for_gcond
2057 find known-true and known-false conditions; on_condition is only
2058 called when adding new constraints. */
2059 if (const gcond
*cond_stmt
= dyn_cast
<const gcond
*> (stmt
))
2061 enum tree_code op
= gimple_cond_code (cond_stmt
);
2062 if (op
== EQ_EXPR
|| op
== NE_EXPR
)
2064 tree lhs
= gimple_cond_lhs (cond_stmt
);
2065 tree rhs
= gimple_cond_rhs (cond_stmt
);
2066 if (any_pointer_p (lhs
)
2067 && any_pointer_p (rhs
)
2070 state_t state
= sm_ctxt
->get_state (stmt
, lhs
);
2071 if (assumed_non_null_p (state
))
2072 maybe_complain_about_deref_before_check
2075 (const assumed_non_null_state
*)state
,
2081 if (tree lhs
= sm_ctxt
->is_zero_assignment (stmt
))
2082 if (any_pointer_p (lhs
))
2083 on_zero_assignment (sm_ctxt
, stmt
,lhs
);
2085 /* Handle dereferences. */
2086 for (unsigned i
= 0; i
< gimple_num_ops (stmt
); i
++)
2088 tree op
= gimple_op (stmt
, i
);
2091 if (TREE_CODE (op
) == COMPONENT_REF
)
2092 op
= TREE_OPERAND (op
, 0);
2094 if (TREE_CODE (op
) == MEM_REF
)
2096 tree arg
= TREE_OPERAND (op
, 0);
2098 state_t state
= sm_ctxt
->get_state (stmt
, arg
);
2099 if (state
== m_start
)
2100 maybe_assume_non_null (sm_ctxt
, arg
, stmt
);
2101 else if (unchecked_p (state
))
2103 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
2104 sm_ctxt
->warn (node
, stmt
, arg
,
2105 make_unique
<possible_null_deref
> (*this,
2107 const allocation_state
*astate
= as_a_allocation_state (state
);
2108 sm_ctxt
->set_next_state (stmt
, arg
, astate
->get_nonnull ());
2110 else if (state
== m_null
)
2112 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
2113 sm_ctxt
->warn (node
, stmt
, arg
,
2114 make_unique
<null_deref
> (*this, diag_arg
));
2115 sm_ctxt
->set_next_state (stmt
, arg
, m_stop
);
2117 else if (freed_p (state
))
2119 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
2120 const allocation_state
*astate
= as_a_allocation_state (state
);
2121 sm_ctxt
->warn (node
, stmt
, arg
,
2122 make_unique
<use_after_free
>
2123 (*this, diag_arg
, astate
->m_deallocator
));
2124 sm_ctxt
->set_next_state (stmt
, arg
, m_stop
);
2131 /* Given a check against null of PTR in assumed-non-null state STATE,
2132 potentially add a deref_before_check warning to SM_CTXT. */
2135 malloc_state_machine::
2136 maybe_complain_about_deref_before_check (sm_context
*sm_ctxt
,
2137 const supernode
*node
,
2139 const assumed_non_null_state
*state
,
2142 const region_model
*model
= sm_ctxt
->get_old_region_model ();
2146 /* Don't complain if the current frame (where the check is occurring) is
2147 deeper than the frame in which the "not null" assumption was made.
2148 This suppress false positives for cases like:
2150 void foo (struct s *p)
2152 int val = s->some_field; // deref here
2156 where "shared_helper" has:
2158 void shared_helper (struct s *p)
2160 if (!p) // check here
2165 since the check in "shared_helper" is OK. */
2166 const frame_region
*checked_in_frame
= model
->get_current_frame ();
2167 const frame_region
*assumed_nonnull_in_frame
= state
->m_frame
;
2168 if (checked_in_frame
->get_index () > assumed_nonnull_in_frame
->get_index ())
2171 tree diag_ptr
= sm_ctxt
->get_diagnostic_tree (ptr
);
2175 make_unique
<deref_before_check
> (*this, diag_ptr
));
2176 sm_ctxt
->set_next_state (stmt
, ptr
, m_stop
);
2179 /* Handle a call to an allocator.
2180 RETURNS_NONNULL is true if CALL is to a fndecl known to have
2181 __attribute__((returns_nonnull)). */
2184 malloc_state_machine::on_allocator_call (sm_context
*sm_ctxt
,
2186 const deallocator_set
*deallocators
,
2187 bool returns_nonnull
) const
2189 tree lhs
= gimple_call_lhs (call
);
2192 if (sm_ctxt
->get_state (call
, lhs
) == m_start
)
2193 sm_ctxt
->set_next_state (call
, lhs
,
2195 ? deallocators
->m_nonnull
2196 : deallocators
->m_unchecked
));
2200 /* TODO: report leak. */
2204 /* Handle deallocations of non-heap pointers.
2205 non-heap -> stop, with warning. */
2208 malloc_state_machine::handle_free_of_non_heap (sm_context
*sm_ctxt
,
2209 const supernode
*node
,
2212 const deallocator
*d
) const
2214 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
2215 const region
*freed_reg
= NULL
;
2216 if (const program_state
*old_state
= sm_ctxt
->get_old_program_state ())
2218 const region_model
*old_model
= old_state
->m_region_model
;
2219 const svalue
*ptr_sval
= old_model
->get_rvalue (arg
, NULL
);
2220 freed_reg
= old_model
->deref_rvalue (ptr_sval
, arg
, NULL
);
2222 sm_ctxt
->warn (node
, call
, arg
,
2223 make_unique
<free_of_non_heap
>
2224 (*this, diag_arg
, freed_reg
, d
->m_name
));
2225 sm_ctxt
->set_next_state (call
, arg
, m_stop
);
2229 malloc_state_machine::on_deallocator_call (sm_context
*sm_ctxt
,
2230 const supernode
*node
,
2232 const deallocator
*d
,
2233 unsigned argno
) const
2235 if (argno
>= gimple_call_num_args (call
))
2237 tree arg
= gimple_call_arg (call
, argno
);
2239 state_t state
= sm_ctxt
->get_state (call
, arg
);
2241 /* start/assumed_non_null/unchecked/nonnull -> freed. */
2242 if (state
== m_start
|| assumed_non_null_p (state
))
2243 sm_ctxt
->set_next_state (call
, arg
, d
->m_freed
);
2244 else if (unchecked_p (state
) || nonnull_p (state
))
2246 const allocation_state
*astate
= as_a_allocation_state (state
);
2247 gcc_assert (astate
->m_deallocators
);
2248 if (!astate
->m_deallocators
->contains_p (d
))
2250 /* Wrong allocator. */
2251 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
2252 sm_ctxt
->warn (node
, call
, arg
,
2253 make_unique
<mismatching_deallocation
>
2255 astate
->m_deallocators
,
2258 sm_ctxt
->set_next_state (call
, arg
, d
->m_freed
);
2261 /* Keep state "null" as-is, rather than transitioning to "freed";
2262 we don't want to complain about double-free of NULL. */
2263 else if (state
== d
->m_freed
)
2265 /* freed -> stop, with warning. */
2266 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
2267 sm_ctxt
->warn (node
, call
, arg
,
2268 make_unique
<double_free
> (*this, diag_arg
, d
->m_name
));
2269 sm_ctxt
->set_next_state (call
, arg
, m_stop
);
2271 else if (state
== m_non_heap
)
2273 /* non-heap -> stop, with warning. */
2274 handle_free_of_non_heap (sm_ctxt
, node
, call
, arg
, d
);
2278 /* Handle a call to "realloc".
2279 Check for free of non-heap or mismatching allocators,
2280 transitioning to the "stop" state for such cases.
2282 Otherwise, kf_realloc::impl_call_post will later
2283 get called (which will handle other sm-state transitions
2284 when the state is bifurcated). */
2287 malloc_state_machine::on_realloc_call (sm_context
*sm_ctxt
,
2288 const supernode
*node
,
2289 const gcall
*call
) const
2291 const unsigned argno
= 0;
2292 const deallocator
*d
= &m_realloc
;
2294 tree arg
= gimple_call_arg (call
, argno
);
2296 state_t state
= sm_ctxt
->get_state (call
, arg
);
2298 if (unchecked_p (state
) || nonnull_p (state
))
2300 const allocation_state
*astate
= as_a_allocation_state (state
);
2301 gcc_assert (astate
->m_deallocators
);
2302 if (!astate
->m_deallocators
->contains_p (&m_free
.m_deallocator
))
2304 /* Wrong allocator. */
2305 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
2306 sm_ctxt
->warn (node
, call
, arg
,
2307 make_unique
<mismatching_deallocation
>
2309 astate
->m_deallocators
, d
));
2310 sm_ctxt
->set_next_state (call
, arg
, m_stop
);
2311 if (path_context
*path_ctxt
= sm_ctxt
->get_path_context ())
2312 path_ctxt
->terminate_path ();
2315 else if (state
== m_free
.m_deallocator
.m_freed
)
2317 /* freed -> stop, with warning. */
2318 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
2319 sm_ctxt
->warn (node
, call
, arg
,
2320 make_unique
<double_free
> (*this, diag_arg
, "free"));
2321 sm_ctxt
->set_next_state (call
, arg
, m_stop
);
2322 if (path_context
*path_ctxt
= sm_ctxt
->get_path_context ())
2323 path_ctxt
->terminate_path ();
2325 else if (state
== m_non_heap
)
2327 /* non-heap -> stop, with warning. */
2328 handle_free_of_non_heap (sm_ctxt
, node
, call
, arg
, d
);
2329 if (path_context
*path_ctxt
= sm_ctxt
->get_path_context ())
2330 path_ctxt
->terminate_path ();
2334 /* Implementation of state_machine::on_phi vfunc for malloc_state_machine. */
2337 malloc_state_machine::on_phi (sm_context
*sm_ctxt
,
2338 const supernode
*node ATTRIBUTE_UNUSED
,
2344 tree lhs
= gimple_phi_result (phi
);
2345 on_zero_assignment (sm_ctxt
, phi
, lhs
);
2349 /* Implementation of state_machine::on_condition vfunc for malloc_state_machine.
2350 Potentially transition state 'unchecked' to 'nonnull' or to 'null'. */
2353 malloc_state_machine::on_condition (sm_context
*sm_ctxt
,
2354 const supernode
*node ATTRIBUTE_UNUSED
,
2358 const svalue
*rhs
) const
2360 if (!rhs
->all_zeroes_p ())
2363 if (!any_pointer_p (lhs
))
2365 if (!any_pointer_p (rhs
))
2370 log ("got 'ARG != 0' match");
2371 state_t s
= sm_ctxt
->get_state (stmt
, lhs
);
2372 if (unchecked_p (s
))
2374 const allocation_state
*astate
= as_a_allocation_state (s
);
2375 sm_ctxt
->set_next_state (stmt
, lhs
, astate
->get_nonnull ());
2378 else if (op
== EQ_EXPR
)
2380 log ("got 'ARG == 0' match");
2381 state_t s
= sm_ctxt
->get_state (stmt
, lhs
);
2382 if (unchecked_p (s
))
2383 sm_ctxt
->set_next_state (stmt
, lhs
, m_null
);
2387 /* Implementation of state_machine::on_pop_frame vfunc for malloc_state_machine.
2388 Clear any "assumed-non-null" state where the assumption happened in
2392 malloc_state_machine::on_pop_frame (sm_state_map
*smap
,
2393 const frame_region
*frame_reg
) const
2395 hash_set
<const svalue
*> svals_to_clear
;
2396 for (auto kv
: *smap
)
2398 const svalue
*sval
= kv
.first
;
2399 state_t state
= kv
.second
.m_state
;
2400 if (assumed_non_null_p (state
))
2402 const assumed_non_null_state
*assumed_state
2403 = (const assumed_non_null_state
*)state
;
2404 if (frame_reg
== assumed_state
->m_frame
)
2405 svals_to_clear
.add (sval
);
2408 for (auto sval
: svals_to_clear
)
2409 smap
->clear_any_state (sval
);
2412 /* Implementation of state_machine::can_purge_p vfunc for malloc_state_machine.
2413 Don't allow purging of pointers in state 'unchecked' or 'nonnull'
2414 (to avoid false leak reports). */
2417 malloc_state_machine::can_purge_p (state_t s
) const
2419 enum resource_state rs
= get_rs (s
);
2420 return rs
!= RS_UNCHECKED
&& rs
!= RS_NONNULL
;
2423 /* Implementation of state_machine::on_leak vfunc for malloc_state_machine
2424 (for complaining about leaks of pointers in state 'unchecked' and
2427 std::unique_ptr
<pending_diagnostic
>
2428 malloc_state_machine::on_leak (tree var
) const
2430 return make_unique
<malloc_leak
> (*this, var
);
2433 /* Implementation of state_machine::reset_when_passed_to_unknown_fn_p vfunc
2434 for malloc_state_machine. */
2437 malloc_state_machine::reset_when_passed_to_unknown_fn_p (state_t s
,
2438 bool is_mutable
) const
2440 /* An on-stack ptr doesn't stop being stack-allocated when passed to an
2442 if (s
== m_non_heap
)
2445 /* Otherwise, pointers passed as non-const can be freed. */
2449 /* Implementation of state_machine::maybe_get_merged_states_nonequal vfunc
2450 for malloc_state_machine.
2452 Support discarding "assumed-non-null" states when merging with
2455 state_machine::state_t
2456 malloc_state_machine::maybe_get_merged_states_nonequal (state_t state_a
,
2457 state_t state_b
) const
2459 if (assumed_non_null_p (state_a
) && state_b
== m_start
)
2461 if (state_a
== m_start
&& assumed_non_null_p (state_b
))
2466 /* Return true if calls to FNDECL are known to not affect this sm-state. */
2469 malloc_state_machine::unaffected_by_call_p (tree fndecl
)
2471 /* A set of functions that are known to not affect allocation
2472 status, even if we haven't fully modelled the rest of their
2474 static const char * const funcnames
[] = {
2475 /* This array must be kept sorted. */
2478 const size_t count
= ARRAY_SIZE (funcnames
);
2479 function_set
fs (funcnames
, count
);
2481 if (fs
.contains_decl_p (fndecl
))
2487 /* Shared logic for handling GIMPLE_ASSIGNs and GIMPLE_PHIs that
2488 assign zero to LHS. */
2491 malloc_state_machine::on_zero_assignment (sm_context
*sm_ctxt
,
2495 state_t s
= sm_ctxt
->get_state (stmt
, lhs
);
2496 enum resource_state rs
= get_rs (s
);
2498 || rs
== RS_UNCHECKED
2501 sm_ctxt
->set_next_state (stmt
, lhs
, m_null
);
2504 /* Special-case hook for handling realloc, for the "success with move to
2505 a new buffer" case, marking OLD_PTR_SVAL as freed and NEW_PTR_SVAL as
2508 This is similar to on_deallocator_call and on_allocator_call,
2509 but the checks happen in on_realloc_call, and by splitting the states. */
2512 malloc_state_machine::
2513 on_realloc_with_move (region_model
*model
,
2515 const svalue
*old_ptr_sval
,
2516 const svalue
*new_ptr_sval
,
2517 const extrinsic_state
&ext_state
) const
2519 smap
->set_state (model
, old_ptr_sval
,
2520 m_free
.m_deallocator
.m_freed
,
2523 smap
->set_state (model
, new_ptr_sval
,
2528 /* Hook for get_or_create_region_for_heap_alloc for the case when we want
2529 ptr_sval to mark a newly created region as assumed non null on malloc SM. */
2531 malloc_state_machine::transition_ptr_sval_non_null (region_model
*model
,
2533 const svalue
*new_ptr_sval
,
2534 const extrinsic_state
&ext_state
) const
2536 smap
->set_state (model
, new_ptr_sval
, m_free
.m_nonnull
, NULL
, ext_state
);
2539 } // anonymous namespace
2541 /* Internal interface to this file. */
2544 make_malloc_state_machine (logger
*logger
)
2546 return new malloc_state_machine (logger
);
2549 /* Specialcase hook for handling realloc, for use by
2550 kf_realloc::impl_call_post::success_with_move::update_model. */
2553 region_model::on_realloc_with_move (const call_details
&cd
,
2554 const svalue
*old_ptr_sval
,
2555 const svalue
*new_ptr_sval
)
2557 region_model_context
*ctxt
= cd
.get_ctxt ();
2560 const extrinsic_state
*ext_state
= ctxt
->get_ext_state ();
2565 const state_machine
*sm
;
2567 if (!ctxt
->get_malloc_map (&smap
, &sm
, &sm_idx
))
2573 const malloc_state_machine
&malloc_sm
2574 = (const malloc_state_machine
&)*sm
;
2576 malloc_sm
.on_realloc_with_move (this,
2583 /* Moves ptr_sval from start to assumed non-null, for use by
2584 region_model::get_or_create_region_for_heap_alloc. */
2586 region_model::transition_ptr_sval_non_null (region_model_context
*ctxt
,
2587 const svalue
*ptr_sval
)
2591 const extrinsic_state
*ext_state
= ctxt
->get_ext_state ();
2596 const state_machine
*sm
;
2598 if (!ctxt
->get_malloc_map (&smap
, &sm
, &sm_idx
))
2604 const malloc_state_machine
&malloc_sm
= (const malloc_state_machine
&)*sm
;
2606 malloc_sm
.transition_ptr_sval_non_null (this, smap
, ptr_sval
, *ext_state
);
2611 #endif /* #if ENABLE_ANALYZER */