1 /* A state machine for detecting misuses of the malloc/free API.
2 Copyright (C) 2019-2021 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"
55 /* This state machine and its various support classes track allocations
58 It has a few standard allocation/deallocation pairs (e.g. new/delete),
59 and also supports user-defined ones via
60 __attribute__ ((malloc(DEALLOCATOR))).
62 There can be more than one valid deallocator for a given allocator,
64 __attribute__ ((malloc (fclose)))
65 __attribute__ ((malloc (freopen, 3)))
66 FILE* fopen (const char*, const char*);
67 A deallocator_set represents a particular set of valid deallocators.
69 We track the expected deallocator_set for a value, but not the allocation
70 function - there could be more than one allocator per deallocator_set.
71 For example, there could be dozens of allocators for "free" beyond just
72 malloc e.g. calloc, xstrdup, etc. We don't want to explode the number
73 of states by tracking individual allocators in the exploded graph;
74 we merely want to track "this value expects to have 'free' called on it".
75 Perhaps we can reconstruct which allocator was used later, when emitting
76 the path, if it's necessary for precision of wording of diagnostics. */
79 class deallocator_set
;
80 class malloc_state_machine
;
82 /* An enum for discriminating between different kinds of allocation_state. */
86 /* States that are independent of allocator/deallocator. */
88 /* The start state. */
91 /* State for a pointer that's known to be NULL. */
94 /* State for a pointer that's known to not be on the heap (e.g. to a local
98 /* Stop state, for pointers we don't want to track any more. */
101 /* States that relate to a specific deallocator_set. */
103 /* State for a pointer returned from an allocator that hasn't
104 been checked for NULL.
105 It could be a pointer to heap-allocated memory, or could be NULL. */
108 /* State for a pointer returned from an allocator,
109 known to be non-NULL. */
112 /* State for a pointer passed to a deallocator. */
116 /* Custom state subclass, which can optionally refer to an a
119 struct allocation_state
: public state_machine::state
121 allocation_state (const char *name
, unsigned id
,
122 enum resource_state rs
,
123 const deallocator_set
*deallocators
,
124 const deallocator
*deallocator
)
125 : state (name
, id
), m_rs (rs
),
126 m_deallocators (deallocators
),
127 m_deallocator (deallocator
)
130 void dump_to_pp (pretty_printer
*pp
) const FINAL OVERRIDE
;
132 const allocation_state
*get_nonnull () const;
134 enum resource_state m_rs
;
135 const deallocator_set
*m_deallocators
;
136 const deallocator
*m_deallocator
;
139 /* An enum for choosing which wording to use in various diagnostics
140 when describing deallocations. */
150 /* Base class representing a deallocation function,
151 either a built-in one we know about, or one exposed via
152 __attribute__((malloc(DEALLOCATOR))). */
156 hashval_t
hash () const;
157 void dump_to_pp (pretty_printer
*pp
) const;
158 static int cmp (const deallocator
*a
, const deallocator
*b
);
159 static int cmp_ptr_ptr (const void *, const void *);
161 /* Name to use in diagnostics. */
164 /* Which wording to use in diagnostics. */
165 enum wording m_wording
;
167 /* State for a value passed to one of the deallocators. */
168 state_machine::state_t m_freed
;
171 deallocator (malloc_state_machine
*sm
,
173 enum wording wording
);
176 /* Subclass representing a predefined deallocator.
177 e.g. "delete []", without needing a specific FUNCTION_DECL
180 struct standard_deallocator
: public deallocator
182 standard_deallocator (malloc_state_machine
*sm
,
184 enum wording wording
);
187 /* Subclass representing a user-defined deallocator
188 via __attribute__((malloc(DEALLOCATOR))) given
189 a specific FUNCTION_DECL. */
191 struct custom_deallocator
: public deallocator
193 custom_deallocator (malloc_state_machine
*sm
,
194 tree deallocator_fndecl
,
195 enum wording wording
)
196 : deallocator (sm
, IDENTIFIER_POINTER (DECL_NAME (deallocator_fndecl
)),
202 /* Base class representing a set of possible deallocators.
203 Often this will be just a single deallocator, but some
204 allocators have multiple valid deallocators (e.g. the result of
205 "fopen" can be closed by either "fclose" or "freopen"). */
207 struct deallocator_set
209 deallocator_set (malloc_state_machine
*sm
,
210 enum wording wording
);
211 virtual ~deallocator_set () {}
213 virtual bool contains_p (const deallocator
*d
) const = 0;
214 virtual const deallocator
*maybe_get_single () const = 0;
215 virtual void dump_to_pp (pretty_printer
*pp
) const = 0;
218 /* Which wording to use in diagnostics. */
219 enum wording m_wording
;
221 /* Pointers to states.
222 These states are owned by the state_machine base class. */
224 /* State for an unchecked result from an allocator using this set. */
225 state_machine::state_t m_unchecked
;
227 /* State for a known non-NULL result from such an allocator. */
228 state_machine::state_t m_nonnull
;
231 /* Subclass of deallocator_set representing a set of deallocators
232 defined by one or more __attribute__((malloc(DEALLOCATOR))). */
234 struct custom_deallocator_set
: public deallocator_set
236 typedef const auto_vec
<const deallocator
*> *key_t
;
238 custom_deallocator_set (malloc_state_machine
*sm
,
239 const auto_vec
<const deallocator
*> *vec
,
241 //const char *dealloc_funcname,
243 enum wording wording
);
245 bool contains_p (const deallocator
*d
) const FINAL OVERRIDE
;
246 const deallocator
*maybe_get_single () const FINAL OVERRIDE
;
247 void dump_to_pp (pretty_printer
*pp
) const FINAL OVERRIDE
;
249 auto_vec
<const deallocator
*> m_deallocator_vec
;
252 /* Subclass of deallocator_set representing a set of deallocators
253 with a single standard_deallocator, e.g. "delete []". */
255 struct standard_deallocator_set
: public deallocator_set
257 standard_deallocator_set (malloc_state_machine
*sm
,
259 enum wording wording
);
261 bool contains_p (const deallocator
*d
) const FINAL OVERRIDE
;
262 const deallocator
*maybe_get_single () const FINAL OVERRIDE
;
263 void dump_to_pp (pretty_printer
*pp
) const FINAL OVERRIDE
;
265 standard_deallocator m_deallocator
;
268 /* Traits class for ensuring uniqueness of deallocator_sets within
269 malloc_state_machine. */
271 struct deallocator_set_map_traits
273 typedef custom_deallocator_set::key_t key_type
;
274 typedef custom_deallocator_set
*value_type
;
275 typedef custom_deallocator_set
*compare_type
;
277 static inline hashval_t
hash (const key_type
&k
)
279 gcc_assert (k
!= NULL
);
280 gcc_assert (k
!= reinterpret_cast<key_type
> (1));
282 hashval_t result
= 0;
284 const deallocator
*d
;
285 FOR_EACH_VEC_ELT (*k
, i
, d
)
286 result
^= d
->hash ();
289 static inline bool equal_keys (const key_type
&k1
, const key_type
&k2
)
291 if (k1
->length () != k2
->length ())
294 for (unsigned i
= 0; i
< k1
->length (); i
++)
295 if ((*k1
)[i
] != (*k2
)[i
])
300 template <typename T
>
301 static inline void remove (T
&)
303 /* empty; the nodes are handled elsewhere. */
305 template <typename T
>
306 static inline void mark_deleted (T
&entry
)
308 entry
.m_key
= reinterpret_cast<key_type
> (1);
310 template <typename T
>
311 static inline void mark_empty (T
&entry
)
315 template <typename T
>
316 static inline bool is_deleted (const T
&entry
)
318 return entry
.m_key
== reinterpret_cast<key_type
> (1);
320 template <typename T
>
321 static inline bool is_empty (const T
&entry
)
323 return entry
.m_key
== NULL
;
325 static const bool empty_zero_p
= false;
328 /* A state machine for detecting misuses of the malloc/free API.
330 See sm-malloc.dot for an overview (keep this in-sync with that file). */
332 class malloc_state_machine
: public state_machine
335 typedef allocation_state custom_data_t
;
337 malloc_state_machine (logger
*logger
);
338 ~malloc_state_machine ();
341 add_state (const char *name
, enum resource_state rs
,
342 const deallocator_set
*deallocators
,
343 const deallocator
*deallocator
);
345 bool inherited_state_p () const FINAL OVERRIDE
{ return false; }
347 state_machine::state_t
348 get_default_state (const svalue
*sval
) const FINAL OVERRIDE
350 if (tree cst
= sval
->maybe_get_constant ())
355 if (const region_svalue
*ptr
= sval
->dyn_cast_region_svalue ())
357 const region
*reg
= ptr
->get_pointee ();
358 const region
*base_reg
= reg
->get_base_region ();
359 if (base_reg
->get_kind () == RK_DECL
360 || base_reg
->get_kind () == RK_STRING
)
366 bool on_stmt (sm_context
*sm_ctxt
,
367 const supernode
*node
,
368 const gimple
*stmt
) const FINAL OVERRIDE
;
370 void on_phi (sm_context
*sm_ctxt
,
371 const supernode
*node
,
373 tree rhs
) const FINAL OVERRIDE
;
375 void on_condition (sm_context
*sm_ctxt
,
376 const supernode
*node
,
380 const svalue
*rhs
) const FINAL OVERRIDE
;
382 bool can_purge_p (state_t s
) const FINAL OVERRIDE
;
383 pending_diagnostic
*on_leak (tree var
) const FINAL OVERRIDE
;
385 bool reset_when_passed_to_unknown_fn_p (state_t s
,
386 bool is_mutable
) const FINAL OVERRIDE
;
388 static bool unaffected_by_call_p (tree fndecl
);
390 standard_deallocator_set m_free
;
391 standard_deallocator_set m_scalar_delete
;
392 standard_deallocator_set m_vector_delete
;
394 standard_deallocator m_realloc
;
396 /* States that are independent of api. */
398 /* State for a pointer that's known to be NULL. */
401 /* State for a pointer that's known to not be on the heap (e.g. to a local
403 state_t m_non_heap
; // TODO: or should this be a different state machine?
404 // or do we need child values etc?
406 /* Stop state, for pointers we don't want to track any more. */
410 const custom_deallocator_set
*
411 get_or_create_custom_deallocator_set (tree allocator_fndecl
);
412 custom_deallocator_set
*
413 maybe_create_custom_deallocator_set (tree allocator_fndecl
);
415 get_or_create_deallocator (tree deallocator_fndecl
);
417 void on_allocator_call (sm_context
*sm_ctxt
,
419 const deallocator_set
*deallocators
,
420 bool returns_nonnull
= false) const;
421 void on_deallocator_call (sm_context
*sm_ctxt
,
422 const supernode
*node
,
424 const deallocator
*d
,
425 unsigned argno
) const;
426 void on_realloc_call (sm_context
*sm_ctxt
,
427 const supernode
*node
,
428 const gcall
*call
) const;
429 void on_zero_assignment (sm_context
*sm_ctxt
,
433 /* A map for consolidating deallocators so that they are
434 unique per deallocator FUNCTION_DECL. */
435 typedef hash_map
<tree
, deallocator
*> deallocator_map_t
;
436 deallocator_map_t m_deallocator_map
;
438 /* Memoized lookups from FUNCTION_DECL to custom_deallocator_set *. */
439 typedef hash_map
<tree
, custom_deallocator_set
*> deallocator_set_cache_t
;
440 deallocator_set_cache_t m_custom_deallocator_set_cache
;
442 /* A map for consolidating custom_deallocator_set instances. */
443 typedef hash_map
<custom_deallocator_set::key_t
,
444 custom_deallocator_set
*,
445 deallocator_set_map_traits
> custom_deallocator_set_map_t
;
446 custom_deallocator_set_map_t m_custom_deallocator_set_map
;
448 /* Record of dynamically-allocated objects, for cleanup. */
449 auto_vec
<custom_deallocator_set
*> m_dynamic_sets
;
450 auto_vec
<custom_deallocator
*> m_dynamic_deallocators
;
453 /* struct deallocator. */
455 deallocator::deallocator (malloc_state_machine
*sm
,
457 enum wording wording
)
460 m_freed (sm
->add_state ("freed", RS_FREED
, NULL
, this))
465 deallocator::hash () const
467 return (hashval_t
)m_freed
->get_id ();
471 deallocator::dump_to_pp (pretty_printer
*pp
) const
473 pp_printf (pp
, "%qs", m_name
);
477 deallocator::cmp (const deallocator
*a
, const deallocator
*b
)
479 return (int)a
->m_freed
->get_id () - (int)b
->m_freed
->get_id ();
483 deallocator::cmp_ptr_ptr (const void *a
, const void *b
)
485 return cmp (*(const deallocator
* const *)a
,
486 *(const deallocator
* const *)b
);
490 /* struct standard_deallocator : public deallocator. */
492 standard_deallocator::standard_deallocator (malloc_state_machine
*sm
,
494 enum wording wording
)
495 : deallocator (sm
, name
, wording
)
499 /* struct deallocator_set. */
501 deallocator_set::deallocator_set (malloc_state_machine
*sm
,
502 enum wording wording
)
503 : m_wording (wording
),
504 m_unchecked (sm
->add_state ("unchecked", RS_UNCHECKED
, this, NULL
)),
505 m_nonnull (sm
->add_state ("nonnull", RS_NONNULL
, this, NULL
))
509 /* Dump a description of this deallocator_set to stderr. */
512 deallocator_set::dump () const
515 pp_show_color (&pp
) = pp_show_color (global_dc
->printer
);
516 pp
.buffer
->stream
= stderr
;
522 /* struct custom_deallocator_set : public deallocator_set. */
524 custom_deallocator_set::
525 custom_deallocator_set (malloc_state_machine
*sm
,
526 const auto_vec
<const deallocator
*> *vec
,
527 enum wording wording
)
528 : deallocator_set (sm
, wording
),
529 m_deallocator_vec (vec
->length ())
532 const deallocator
*d
;
533 FOR_EACH_VEC_ELT (*vec
, i
, d
)
534 m_deallocator_vec
.safe_push (d
);
538 custom_deallocator_set::contains_p (const deallocator
*d
) const
541 const deallocator
*cd
;
542 FOR_EACH_VEC_ELT (m_deallocator_vec
, i
, cd
)
549 custom_deallocator_set::maybe_get_single () const
551 if (m_deallocator_vec
.length () == 1)
552 return m_deallocator_vec
[0];
557 custom_deallocator_set::dump_to_pp (pretty_printer
*pp
) const
559 pp_character (pp
, '{');
561 const deallocator
*d
;
562 FOR_EACH_VEC_ELT (m_deallocator_vec
, i
, d
)
565 pp_string (pp
, ", ");
568 pp_character (pp
, '}');
571 /* struct standard_deallocator_set : public deallocator_set. */
573 standard_deallocator_set::standard_deallocator_set (malloc_state_machine
*sm
,
575 enum wording wording
)
576 : deallocator_set (sm
, wording
),
577 m_deallocator (sm
, name
, wording
)
582 standard_deallocator_set::contains_p (const deallocator
*d
) const
584 return d
== &m_deallocator
;
588 standard_deallocator_set::maybe_get_single () const
590 return &m_deallocator
;
594 standard_deallocator_set::dump_to_pp (pretty_printer
*pp
) const
596 pp_character (pp
, '{');
597 pp_string (pp
, m_deallocator
.m_name
);
598 pp_character (pp
, '}');
601 /* Return STATE cast to the custom state subclass, or NULL for the start state.
602 Everything should be an allocation_state apart from the start state. */
604 static const allocation_state
*
605 dyn_cast_allocation_state (state_machine::state_t state
)
607 if (state
->get_id () == 0)
609 return static_cast <const allocation_state
*> (state
);
612 /* Return STATE cast to the custom state subclass, for a state that is
613 already known to not be the start state . */
615 static const allocation_state
*
616 as_a_allocation_state (state_machine::state_t state
)
618 gcc_assert (state
->get_id () != 0);
619 return static_cast <const allocation_state
*> (state
);
622 /* Get the resource_state for STATE. */
624 static enum resource_state
625 get_rs (state_machine::state_t state
)
627 if (const allocation_state
*astate
= dyn_cast_allocation_state (state
))
633 /* Return true if STATE is the start state. */
636 start_p (state_machine::state_t state
)
638 return get_rs (state
) == RS_START
;
641 /* Return true if STATE is an unchecked result from an allocator. */
644 unchecked_p (state_machine::state_t state
)
646 return get_rs (state
) == RS_UNCHECKED
;
649 /* Return true if STATE is a non-null result from an allocator. */
652 nonnull_p (state_machine::state_t state
)
654 return get_rs (state
) == RS_NONNULL
;
657 /* Return true if STATE is a value that has been passed to a deallocator. */
660 freed_p (state_machine::state_t state
)
662 return get_rs (state
) == RS_FREED
;
665 /* Class for diagnostics relating to malloc_state_machine. */
667 class malloc_diagnostic
: public pending_diagnostic
670 malloc_diagnostic (const malloc_state_machine
&sm
, tree arg
)
671 : m_sm (sm
), m_arg (arg
)
674 bool subclass_equal_p (const pending_diagnostic
&base_other
) const OVERRIDE
676 return same_tree_p (m_arg
, ((const malloc_diagnostic
&)base_other
).m_arg
);
679 label_text
describe_state_change (const evdesc::state_change
&change
)
682 if (change
.m_old_state
== m_sm
.get_start_state ()
683 && unchecked_p (change
.m_new_state
))
684 // TODO: verify that it's the allocation stmt, not a copy
685 return label_text::borrow ("allocated here");
686 if (unchecked_p (change
.m_old_state
)
687 && nonnull_p (change
.m_new_state
))
690 return change
.formatted_print ("assuming %qE is non-NULL",
693 return change
.formatted_print ("assuming %qs is non-NULL",
696 if (change
.m_new_state
== m_sm
.m_null
)
698 if (unchecked_p (change
.m_old_state
))
701 return change
.formatted_print ("assuming %qE is NULL",
704 return change
.formatted_print ("assuming %qs is NULL",
710 return change
.formatted_print ("%qE is NULL",
713 return change
.formatted_print ("%qs is NULL",
718 return label_text ();
722 const malloc_state_machine
&m_sm
;
726 /* Concrete subclass for reporting mismatching allocator/deallocator
729 class mismatching_deallocation
: public malloc_diagnostic
732 mismatching_deallocation (const malloc_state_machine
&sm
, tree arg
,
733 const deallocator_set
*expected_deallocators
,
734 const deallocator
*actual_dealloc
)
735 : malloc_diagnostic (sm
, arg
),
736 m_expected_deallocators (expected_deallocators
),
737 m_actual_dealloc (actual_dealloc
)
740 const char *get_kind () const FINAL OVERRIDE
742 return "mismatching_deallocation";
745 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
747 auto_diagnostic_group d
;
748 diagnostic_metadata m
;
749 m
.add_cwe (762); /* CWE-762: Mismatched Memory Management Routines. */
750 if (const deallocator
*expected_dealloc
751 = m_expected_deallocators
->maybe_get_single ())
752 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_mismatching_deallocation
,
753 "%qE should have been deallocated with %qs"
754 " but was deallocated with %qs",
755 m_arg
, expected_dealloc
->m_name
,
756 m_actual_dealloc
->m_name
);
758 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_mismatching_deallocation
,
759 "%qs called on %qE returned from a mismatched"
760 " allocation function",
761 m_actual_dealloc
->m_name
, m_arg
);
764 label_text
describe_state_change (const evdesc::state_change
&change
)
767 if (unchecked_p (change
.m_new_state
))
769 m_alloc_event
= change
.m_event_id
;
770 if (const deallocator
*expected_dealloc
771 = m_expected_deallocators
->maybe_get_single ())
772 return change
.formatted_print ("allocated here"
773 " (expects deallocation with %qs)",
774 expected_dealloc
->m_name
);
776 return change
.formatted_print ("allocated here");
778 return malloc_diagnostic::describe_state_change (change
);
781 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
783 if (m_alloc_event
.known_p ())
785 if (const deallocator
*expected_dealloc
786 = m_expected_deallocators
->maybe_get_single ())
787 return ev
.formatted_print
788 ("deallocated with %qs here;"
789 " allocation at %@ expects deallocation with %qs",
790 m_actual_dealloc
->m_name
, &m_alloc_event
,
791 expected_dealloc
->m_name
);
793 return ev
.formatted_print
794 ("deallocated with %qs here;"
796 m_actual_dealloc
->m_name
, &m_alloc_event
);
798 return ev
.formatted_print ("deallocated with %qs here",
799 m_actual_dealloc
->m_name
);
803 diagnostic_event_id_t m_alloc_event
;
804 const deallocator_set
*m_expected_deallocators
;
805 const deallocator
*m_actual_dealloc
;
808 /* Concrete subclass for reporting double-free diagnostics. */
810 class double_free
: public malloc_diagnostic
813 double_free (const malloc_state_machine
&sm
, tree arg
, const char *funcname
)
814 : malloc_diagnostic (sm
, arg
), m_funcname (funcname
)
817 const char *get_kind () const FINAL OVERRIDE
{ return "double_free"; }
819 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
821 auto_diagnostic_group d
;
822 diagnostic_metadata m
;
823 m
.add_cwe (415); /* CWE-415: Double Free. */
824 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_double_free
,
825 "double-%qs of %qE", m_funcname
, m_arg
);
828 label_text
describe_state_change (const evdesc::state_change
&change
)
831 if (freed_p (change
.m_new_state
))
833 m_first_free_event
= change
.m_event_id
;
834 return change
.formatted_print ("first %qs here", m_funcname
);
836 return malloc_diagnostic::describe_state_change (change
);
839 label_text
describe_call_with_state (const evdesc::call_with_state
&info
)
842 if (freed_p (info
.m_state
))
843 return info
.formatted_print
844 ("passing freed pointer %qE in call to %qE from %qE",
845 info
.m_expr
, info
.m_callee_fndecl
, info
.m_caller_fndecl
);
846 return label_text ();
849 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
851 if (m_first_free_event
.known_p ())
852 return ev
.formatted_print ("second %qs here; first %qs was at %@",
853 m_funcname
, m_funcname
,
854 &m_first_free_event
);
855 return ev
.formatted_print ("second %qs here", m_funcname
);
859 diagnostic_event_id_t m_first_free_event
;
860 const char *m_funcname
;
863 /* Abstract subclass for describing possible bad uses of NULL.
864 Responsible for describing the call that could return NULL. */
866 class possible_null
: public malloc_diagnostic
869 possible_null (const malloc_state_machine
&sm
, tree arg
)
870 : malloc_diagnostic (sm
, arg
)
873 label_text
describe_state_change (const evdesc::state_change
&change
)
876 if (change
.m_old_state
== m_sm
.get_start_state ()
877 && unchecked_p (change
.m_new_state
))
879 m_origin_of_unchecked_event
= change
.m_event_id
;
880 return label_text::borrow ("this call could return NULL");
882 return malloc_diagnostic::describe_state_change (change
);
885 label_text
describe_return_of_state (const evdesc::return_of_state
&info
)
888 if (unchecked_p (info
.m_state
))
889 return info
.formatted_print ("possible return of NULL to %qE from %qE",
890 info
.m_caller_fndecl
, info
.m_callee_fndecl
);
891 return label_text ();
895 diagnostic_event_id_t m_origin_of_unchecked_event
;
898 /* Concrete subclass for describing dereference of a possible NULL
901 class possible_null_deref
: public possible_null
904 possible_null_deref (const malloc_state_machine
&sm
, tree arg
)
905 : possible_null (sm
, arg
)
908 const char *get_kind () const FINAL OVERRIDE
{ return "possible_null_deref"; }
910 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
912 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
913 diagnostic_metadata m
;
915 return warning_meta (rich_loc
, m
,
916 OPT_Wanalyzer_possible_null_dereference
,
917 "dereference of possibly-NULL %qE", m_arg
);
920 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
922 if (m_origin_of_unchecked_event
.known_p ())
923 return ev
.formatted_print ("%qE could be NULL: unchecked value from %@",
925 &m_origin_of_unchecked_event
);
927 return ev
.formatted_print ("%qE could be NULL", ev
.m_expr
);
932 /* Return true if FNDECL is a C++ method. */
935 method_p (tree fndecl
)
937 return TREE_CODE (TREE_TYPE (fndecl
)) == METHOD_TYPE
;
940 /* Return a 1-based description of ARG_IDX (0-based) of FNDECL.
941 Compare with %P in the C++ FE (implemented in cp/error.c: parm_to_string
942 as called from cp_printer). */
945 describe_argument_index (tree fndecl
, int arg_idx
)
947 if (method_p (fndecl
))
949 return label_text::borrow ("'this'");
951 pp_printf (&pp
, "%u", arg_idx
+ 1 - method_p (fndecl
));
952 return label_text::take (xstrdup (pp_formatted_text (&pp
)));
955 /* Subroutine for use by possible_null_arg::emit and null_arg::emit.
956 Issue a note informing that the pertinent argument must be non-NULL. */
959 inform_nonnull_attribute (tree fndecl
, int arg_idx
)
961 label_text arg_desc
= describe_argument_index (fndecl
, arg_idx
);
962 inform (DECL_SOURCE_LOCATION (fndecl
),
963 "argument %s of %qD must be non-null",
964 arg_desc
.m_buffer
, fndecl
);
965 arg_desc
.maybe_free ();
966 /* Ideally we would use the location of the parm and underline the
967 attribute also - but we don't have the location_t values at this point
969 For reference, the C and C++ FEs have get_fndecl_argument_location. */
972 /* Concrete subclass for describing passing a possibly-NULL value to a
973 function marked with __attribute__((nonnull)). */
975 class possible_null_arg
: public possible_null
978 possible_null_arg (const malloc_state_machine
&sm
, tree arg
,
979 tree fndecl
, int arg_idx
)
980 : possible_null (sm
, arg
),
981 m_fndecl (fndecl
), m_arg_idx (arg_idx
)
984 const char *get_kind () const FINAL OVERRIDE
{ return "possible_null_arg"; }
986 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
988 const possible_null_arg
&sub_other
989 = (const possible_null_arg
&)base_other
;
990 return (same_tree_p (m_arg
, sub_other
.m_arg
)
991 && m_fndecl
== sub_other
.m_fndecl
992 && m_arg_idx
== sub_other
.m_arg_idx
);
996 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
998 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
999 auto_diagnostic_group d
;
1000 diagnostic_metadata m
;
1003 = warning_meta (rich_loc
, m
, OPT_Wanalyzer_possible_null_argument
,
1004 "use of possibly-NULL %qE where non-null expected",
1007 inform_nonnull_attribute (m_fndecl
, m_arg_idx
);
1011 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
1013 label_text arg_desc
= describe_argument_index (m_fndecl
, m_arg_idx
);
1015 if (m_origin_of_unchecked_event
.known_p ())
1016 result
= ev
.formatted_print ("argument %s (%qE) from %@ could be NULL"
1017 " where non-null expected",
1018 arg_desc
.m_buffer
, ev
.m_expr
,
1019 &m_origin_of_unchecked_event
);
1021 result
= ev
.formatted_print ("argument %s (%qE) could be NULL"
1022 " where non-null expected",
1023 arg_desc
.m_buffer
, ev
.m_expr
);
1024 arg_desc
.maybe_free ();
1033 /* Concrete subclass for describing a dereference of a NULL value. */
1035 class null_deref
: public malloc_diagnostic
1038 null_deref (const malloc_state_machine
&sm
, tree arg
)
1039 : malloc_diagnostic (sm
, arg
) {}
1041 const char *get_kind () const FINAL OVERRIDE
{ return "null_deref"; }
1043 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
1045 /* CWE-476: NULL Pointer Dereference. */
1046 diagnostic_metadata m
;
1048 return warning_meta (rich_loc
, m
,
1049 OPT_Wanalyzer_null_dereference
,
1050 "dereference of NULL %qE", m_arg
);
1053 label_text
describe_return_of_state (const evdesc::return_of_state
&info
)
1056 if (info
.m_state
== m_sm
.m_null
)
1057 return info
.formatted_print ("return of NULL to %qE from %qE",
1058 info
.m_caller_fndecl
, info
.m_callee_fndecl
);
1059 return label_text ();
1062 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
1064 return ev
.formatted_print ("dereference of NULL %qE", ev
.m_expr
);
1068 /* Concrete subclass for describing passing a NULL value to a
1069 function marked with __attribute__((nonnull)). */
1071 class null_arg
: public malloc_diagnostic
1074 null_arg (const malloc_state_machine
&sm
, tree arg
,
1075 tree fndecl
, int arg_idx
)
1076 : malloc_diagnostic (sm
, arg
),
1077 m_fndecl (fndecl
), m_arg_idx (arg_idx
)
1080 const char *get_kind () const FINAL OVERRIDE
{ return "null_arg"; }
1082 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
1084 const null_arg
&sub_other
1085 = (const null_arg
&)base_other
;
1086 return (same_tree_p (m_arg
, sub_other
.m_arg
)
1087 && m_fndecl
== sub_other
.m_fndecl
1088 && m_arg_idx
== sub_other
.m_arg_idx
);
1091 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
1093 /* CWE-476: NULL Pointer Dereference. */
1094 auto_diagnostic_group d
;
1095 diagnostic_metadata m
;
1100 warned
= warning_meta (rich_loc
, m
, OPT_Wanalyzer_null_argument
,
1101 "use of NULL where non-null expected");
1103 warned
= warning_meta (rich_loc
, m
, OPT_Wanalyzer_null_argument
,
1104 "use of NULL %qE where non-null expected",
1107 inform_nonnull_attribute (m_fndecl
, m_arg_idx
);
1111 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
1113 label_text arg_desc
= describe_argument_index (m_fndecl
, m_arg_idx
);
1115 if (zerop (ev
.m_expr
))
1116 result
= ev
.formatted_print ("argument %s NULL where non-null expected",
1119 result
= ev
.formatted_print ("argument %s (%qE) NULL"
1120 " where non-null expected",
1121 arg_desc
.m_buffer
, ev
.m_expr
);
1122 arg_desc
.maybe_free ();
1131 class use_after_free
: public malloc_diagnostic
1134 use_after_free (const malloc_state_machine
&sm
, tree arg
,
1135 const deallocator
*deallocator
)
1136 : malloc_diagnostic (sm
, arg
),
1137 m_deallocator (deallocator
)
1139 gcc_assert (deallocator
);
1142 const char *get_kind () const FINAL OVERRIDE
{ return "use_after_free"; }
1144 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
1146 /* CWE-416: Use After Free. */
1147 diagnostic_metadata m
;
1149 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_use_after_free
,
1150 "use after %<%s%> of %qE",
1151 m_deallocator
->m_name
, m_arg
);
1154 label_text
describe_state_change (const evdesc::state_change
&change
)
1157 if (freed_p (change
.m_new_state
))
1159 m_free_event
= change
.m_event_id
;
1160 switch (m_deallocator
->m_wording
)
1163 case WORDING_REALLOCATED
:
1166 return label_text::borrow ("freed here");
1167 case WORDING_DELETED
:
1168 return label_text::borrow ("deleted here");
1169 case WORDING_DEALLOCATED
:
1170 return label_text::borrow ("deallocated here");
1173 return malloc_diagnostic::describe_state_change (change
);
1176 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
1178 const char *funcname
= m_deallocator
->m_name
;
1179 if (m_free_event
.known_p ())
1180 switch (m_deallocator
->m_wording
)
1183 case WORDING_REALLOCATED
:
1186 return ev
.formatted_print ("use after %<%s%> of %qE; freed at %@",
1187 funcname
, ev
.m_expr
, &m_free_event
);
1188 case WORDING_DELETED
:
1189 return ev
.formatted_print ("use after %<%s%> of %qE; deleted at %@",
1190 funcname
, ev
.m_expr
, &m_free_event
);
1191 case WORDING_DEALLOCATED
:
1192 return ev
.formatted_print ("use after %<%s%> of %qE;"
1193 " deallocated at %@",
1194 funcname
, ev
.m_expr
, &m_free_event
);
1197 return ev
.formatted_print ("use after %<%s%> of %qE",
1198 funcname
, ev
.m_expr
);
1201 /* Implementation of pending_diagnostic::supercedes_p for
1204 We want use-after-free to supercede use-of-unitialized-value,
1205 so that if we have these at the same stmt, we don't emit
1206 a use-of-uninitialized, just the use-after-free.
1207 (this is because we fully purge information about freed
1208 buffers when we free them to avoid state explosions, so
1209 that if they are accessed after the free, it looks like
1210 they are uninitialized). */
1212 bool supercedes_p (const pending_diagnostic
&other
) const FINAL OVERRIDE
1214 if (other
.use_of_uninit_p ())
1221 diagnostic_event_id_t m_free_event
;
1222 const deallocator
*m_deallocator
;
1225 class malloc_leak
: public malloc_diagnostic
1228 malloc_leak (const malloc_state_machine
&sm
, tree arg
)
1229 : malloc_diagnostic (sm
, arg
) {}
1231 const char *get_kind () const FINAL OVERRIDE
{ return "malloc_leak"; }
1233 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
1235 diagnostic_metadata m
;
1238 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_malloc_leak
,
1239 "leak of %qE", m_arg
);
1241 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_malloc_leak
,
1242 "leak of %qs", "<unknown>");
1245 label_text
describe_state_change (const evdesc::state_change
&change
)
1248 if (unchecked_p (change
.m_new_state
)
1249 || (start_p (change
.m_old_state
) && nonnull_p (change
.m_new_state
)))
1251 m_alloc_event
= change
.m_event_id
;
1252 return label_text::borrow ("allocated here");
1254 return malloc_diagnostic::describe_state_change (change
);
1257 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
1261 if (m_alloc_event
.known_p ())
1262 return ev
.formatted_print ("%qE leaks here; was allocated at %@",
1263 ev
.m_expr
, &m_alloc_event
);
1265 return ev
.formatted_print ("%qE leaks here", ev
.m_expr
);
1269 if (m_alloc_event
.known_p ())
1270 return ev
.formatted_print ("%qs leaks here; was allocated at %@",
1271 "<unknown>", &m_alloc_event
);
1273 return ev
.formatted_print ("%qs leaks here", "<unknown>");
1278 diagnostic_event_id_t m_alloc_event
;
1281 class free_of_non_heap
: public malloc_diagnostic
1284 free_of_non_heap (const malloc_state_machine
&sm
, tree arg
,
1285 const char *funcname
)
1286 : malloc_diagnostic (sm
, arg
), m_funcname (funcname
), m_kind (KIND_UNKNOWN
)
1290 const char *get_kind () const FINAL OVERRIDE
{ return "free_of_non_heap"; }
1292 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
1295 const free_of_non_heap
&other
= (const free_of_non_heap
&)base_other
;
1296 return (same_tree_p (m_arg
, other
.m_arg
) && m_kind
== other
.m_kind
);
1299 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
1301 auto_diagnostic_group d
;
1302 diagnostic_metadata m
;
1303 m
.add_cwe (590); /* CWE-590: Free of Memory not on the Heap. */
1309 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_free_of_non_heap
,
1310 "%<%s%> of %qE which points to memory"
1315 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_free_of_non_heap
,
1316 "%<%s%> of memory allocated on the stack by"
1317 " %qs (%qE) will corrupt the heap",
1318 m_funcname
, "alloca", m_arg
);
1323 label_text
describe_state_change (const evdesc::state_change
&change
)
1326 /* Attempt to reconstruct what kind of pointer it is.
1327 (It seems neater for this to be a part of the state, though). */
1328 if (change
.m_expr
&& TREE_CODE (change
.m_expr
) == SSA_NAME
)
1330 gimple
*def_stmt
= SSA_NAME_DEF_STMT (change
.m_expr
);
1331 if (gcall
*call
= dyn_cast
<gcall
*> (def_stmt
))
1333 if (is_special_named_call_p (call
, "alloca", 1)
1334 || is_special_named_call_p (call
, "__builtin_alloca", 1))
1336 m_kind
= KIND_ALLOCA
;
1337 return label_text::borrow
1338 ("memory is allocated on the stack here");
1342 return label_text::borrow ("pointer is from here");
1345 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
1347 return ev
.formatted_print ("call to %qs here", m_funcname
);
1356 const char *m_funcname
;
1360 /* struct allocation_state : public state_machine::state. */
1362 /* Implementation of state_machine::state::dump_to_pp vfunc
1363 for allocation_state: append the API that this allocation is
1367 allocation_state::dump_to_pp (pretty_printer
*pp
) const
1369 state_machine::state::dump_to_pp (pp
);
1372 pp_string (pp
, " (");
1373 m_deallocators
->dump_to_pp (pp
);
1374 pp_character (pp
, ')');
1378 /* Given a allocation_state for a deallocator_set, get the "nonnull" state
1379 for the corresponding allocator(s). */
1381 const allocation_state
*
1382 allocation_state::get_nonnull () const
1384 gcc_assert (m_deallocators
);
1385 return as_a_allocation_state (m_deallocators
->m_nonnull
);
1388 /* malloc_state_machine's ctor. */
1390 malloc_state_machine::malloc_state_machine (logger
*logger
)
1391 : state_machine ("malloc", logger
),
1392 m_free (this, "free", WORDING_FREED
),
1393 m_scalar_delete (this, "delete", WORDING_DELETED
),
1394 m_vector_delete (this, "delete[]", WORDING_DELETED
),
1395 m_realloc (this, "realloc", WORDING_REALLOCATED
)
1397 gcc_assert (m_start
->get_id () == 0);
1398 m_null
= add_state ("null", RS_FREED
, NULL
, NULL
);
1399 m_non_heap
= add_state ("non-heap", RS_NON_HEAP
, NULL
, NULL
);
1400 m_stop
= add_state ("stop", RS_STOP
, NULL
, NULL
);
1403 malloc_state_machine::~malloc_state_machine ()
1406 custom_deallocator_set
*set
;
1407 FOR_EACH_VEC_ELT (m_dynamic_sets
, i
, set
)
1409 custom_deallocator
*d
;
1410 FOR_EACH_VEC_ELT (m_dynamic_deallocators
, i
, d
)
1414 state_machine::state_t
1415 malloc_state_machine::add_state (const char *name
, enum resource_state rs
,
1416 const deallocator_set
*deallocators
,
1417 const deallocator
*deallocator
)
1419 return add_custom_state (new allocation_state (name
, alloc_state_id (),
1424 /* If ALLOCATOR_FNDECL has any "__attribute__((malloc(FOO)))",
1425 return a custom_deallocator_set for them, consolidating them
1426 to ensure uniqueness of the sets.
1428 Return NULL if it has no such attributes. */
1430 const custom_deallocator_set
*
1431 malloc_state_machine::
1432 get_or_create_custom_deallocator_set (tree allocator_fndecl
)
1434 /* Early rejection of decls without attributes. */
1435 tree attrs
= DECL_ATTRIBUTES (allocator_fndecl
);
1439 /* Otherwise, call maybe_create_custom_deallocator_set,
1440 memoizing the result. */
1441 if (custom_deallocator_set
**slot
1442 = m_custom_deallocator_set_cache
.get (allocator_fndecl
))
1444 custom_deallocator_set
*set
1445 = maybe_create_custom_deallocator_set (allocator_fndecl
);
1446 m_custom_deallocator_set_cache
.put (allocator_fndecl
, set
);
1450 /* Given ALLOCATOR_FNDECL, a FUNCTION_DECL with attributes,
1451 look for any "__attribute__((malloc(FOO)))" and return a
1452 custom_deallocator_set for them, consolidating them
1453 to ensure uniqueness of the sets.
1455 Return NULL if it has no such attributes.
1457 Subroutine of get_or_create_custom_deallocator_set which
1458 memoizes the result. */
1460 custom_deallocator_set
*
1461 malloc_state_machine::
1462 maybe_create_custom_deallocator_set (tree allocator_fndecl
)
1464 tree attrs
= DECL_ATTRIBUTES (allocator_fndecl
);
1467 /* Look for instances of __attribute__((malloc(FOO))). */
1468 auto_vec
<const deallocator
*> deallocator_vec
;
1469 for (tree allocs
= attrs
;
1470 (allocs
= lookup_attribute ("malloc", allocs
));
1471 allocs
= TREE_CHAIN (allocs
))
1473 tree args
= TREE_VALUE (allocs
);
1476 if (TREE_VALUE (args
))
1478 const deallocator
*d
1479 = get_or_create_deallocator (TREE_VALUE (args
));
1480 deallocator_vec
.safe_push (d
);
1484 /* If there weren't any deallocators, bail. */
1485 if (deallocator_vec
.length () == 0)
1488 /* Consolidate, so that we reuse existing deallocator_set
1490 deallocator_vec
.qsort (deallocator::cmp_ptr_ptr
);
1491 custom_deallocator_set
**slot
1492 = m_custom_deallocator_set_map
.get (&deallocator_vec
);
1495 custom_deallocator_set
*set
1496 = new custom_deallocator_set (this, &deallocator_vec
, WORDING_DEALLOCATED
);
1497 m_custom_deallocator_set_map
.put (&set
->m_deallocator_vec
, set
);
1498 m_dynamic_sets
.safe_push (set
);
1502 /* Get the deallocator for DEALLOCATOR_FNDECL, creating it if necessary. */
1505 malloc_state_machine::get_or_create_deallocator (tree deallocator_fndecl
)
1507 deallocator
**slot
= m_deallocator_map
.get (deallocator_fndecl
);
1513 if (is_named_call_p (deallocator_fndecl
, "free")
1514 || is_std_named_call_p (deallocator_fndecl
, "free")
1515 || is_named_call_p (deallocator_fndecl
, "__builtin_free"))
1516 d
= &m_free
.m_deallocator
;
1519 custom_deallocator
*cd
1520 = new custom_deallocator (this, deallocator_fndecl
,
1521 WORDING_DEALLOCATED
);
1522 m_dynamic_deallocators
.safe_push (cd
);
1525 m_deallocator_map
.put (deallocator_fndecl
, d
);
1529 /* Implementation of state_machine::on_stmt vfunc for malloc_state_machine. */
1532 malloc_state_machine::on_stmt (sm_context
*sm_ctxt
,
1533 const supernode
*node
,
1534 const gimple
*stmt
) const
1536 if (const gcall
*call
= dyn_cast
<const gcall
*> (stmt
))
1537 if (tree callee_fndecl
= sm_ctxt
->get_fndecl_for_call (call
))
1539 if (is_named_call_p (callee_fndecl
, "malloc", call
, 1)
1540 || is_named_call_p (callee_fndecl
, "calloc", call
, 2)
1541 || is_std_named_call_p (callee_fndecl
, "malloc", call
, 1)
1542 || is_std_named_call_p (callee_fndecl
, "calloc", call
, 2)
1543 || is_named_call_p (callee_fndecl
, "__builtin_malloc", call
, 1)
1544 || is_named_call_p (callee_fndecl
, "__builtin_calloc", call
, 2)
1545 || is_named_call_p (callee_fndecl
, "strdup", call
, 1)
1546 || is_named_call_p (callee_fndecl
, "strndup", call
, 2))
1548 on_allocator_call (sm_ctxt
, call
, &m_free
);
1552 if (is_named_call_p (callee_fndecl
, "operator new", call
, 1))
1553 on_allocator_call (sm_ctxt
, call
, &m_scalar_delete
);
1554 else if (is_named_call_p (callee_fndecl
, "operator new []", call
, 1))
1555 on_allocator_call (sm_ctxt
, call
, &m_vector_delete
);
1556 else if (is_named_call_p (callee_fndecl
, "operator delete", call
, 1)
1557 || is_named_call_p (callee_fndecl
, "operator delete", call
, 2))
1559 on_deallocator_call (sm_ctxt
, node
, call
,
1560 &m_scalar_delete
.m_deallocator
, 0);
1563 else if (is_named_call_p (callee_fndecl
, "operator delete []", call
, 1))
1565 on_deallocator_call (sm_ctxt
, node
, call
,
1566 &m_vector_delete
.m_deallocator
, 0);
1570 if (is_named_call_p (callee_fndecl
, "alloca", call
, 1)
1571 || is_named_call_p (callee_fndecl
, "__builtin_alloca", call
, 1))
1573 tree lhs
= gimple_call_lhs (call
);
1575 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
, m_non_heap
);
1579 if (is_named_call_p (callee_fndecl
, "free", call
, 1)
1580 || is_std_named_call_p (callee_fndecl
, "free", call
, 1)
1581 || is_named_call_p (callee_fndecl
, "__builtin_free", call
, 1))
1583 on_deallocator_call (sm_ctxt
, node
, call
,
1584 &m_free
.m_deallocator
, 0);
1588 if (is_named_call_p (callee_fndecl
, "realloc", call
, 2)
1589 || is_named_call_p (callee_fndecl
, "__builtin_realloc", call
, 2))
1591 on_realloc_call (sm_ctxt
, node
, call
);
1595 if (unaffected_by_call_p (callee_fndecl
))
1598 /* Cast away const-ness for cache-like operations. */
1599 malloc_state_machine
*mutable_this
1600 = const_cast <malloc_state_machine
*> (this);
1602 /* Handle "__attribute__((malloc(FOO)))". */
1603 if (const deallocator_set
*deallocators
1604 = mutable_this
->get_or_create_custom_deallocator_set
1607 tree attrs
= TYPE_ATTRIBUTES (TREE_TYPE (callee_fndecl
));
1608 bool returns_nonnull
1609 = lookup_attribute ("returns_nonnull", attrs
);
1610 on_allocator_call (sm_ctxt
, call
, deallocators
, returns_nonnull
);
1613 /* Handle "__attribute__((nonnull))". */
1615 tree fntype
= TREE_TYPE (callee_fndecl
);
1616 bitmap nonnull_args
= get_nonnull_args (fntype
);
1619 for (unsigned i
= 0; i
< gimple_call_num_args (stmt
); i
++)
1621 tree arg
= gimple_call_arg (stmt
, i
);
1622 if (TREE_CODE (TREE_TYPE (arg
)) != POINTER_TYPE
)
1624 /* If we have a nonnull-args, and either all pointers, or just
1625 the specified pointers. */
1626 if (bitmap_empty_p (nonnull_args
)
1627 || bitmap_bit_p (nonnull_args
, i
))
1629 state_t state
= sm_ctxt
->get_state (stmt
, arg
);
1630 /* Can't use a switch as the states are non-const. */
1631 if (unchecked_p (state
))
1633 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1634 sm_ctxt
->warn (node
, stmt
, arg
,
1635 new possible_null_arg (*this, diag_arg
,
1638 const allocation_state
*astate
1639 = as_a_allocation_state (state
);
1640 sm_ctxt
->set_next_state (stmt
, arg
,
1641 astate
->get_nonnull ());
1643 else if (state
== m_null
)
1645 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1646 sm_ctxt
->warn (node
, stmt
, arg
,
1647 new null_arg (*this, diag_arg
,
1649 sm_ctxt
->set_next_state (stmt
, arg
, m_stop
);
1653 BITMAP_FREE (nonnull_args
);
1657 /* Check for this after nonnull, so that if we have both
1658 then we transition to "freed", rather than "checked". */
1659 unsigned dealloc_argno
= fndecl_dealloc_argno (callee_fndecl
);
1660 if (dealloc_argno
!= UINT_MAX
)
1662 const deallocator
*d
1663 = mutable_this
->get_or_create_deallocator (callee_fndecl
);
1664 on_deallocator_call (sm_ctxt
, node
, call
, d
, dealloc_argno
);
1668 if (tree lhs
= sm_ctxt
->is_zero_assignment (stmt
))
1669 if (any_pointer_p (lhs
))
1670 on_zero_assignment (sm_ctxt
, stmt
,lhs
);
1672 /* If we have "LHS = &EXPR;" and EXPR is something other than a MEM_REF,
1673 transition LHS from start to non_heap.
1674 Doing it for ADDR_EXPR(MEM_REF()) is likely wrong, and can lead to
1675 unbounded chains of unmergeable sm-state on pointer arithmetic in loops
1676 when optimization is enabled. */
1677 if (const gassign
*assign_stmt
= dyn_cast
<const gassign
*> (stmt
))
1679 enum tree_code op
= gimple_assign_rhs_code (assign_stmt
);
1680 if (op
== ADDR_EXPR
)
1682 tree lhs
= gimple_assign_lhs (assign_stmt
);
1685 tree addr_expr
= gimple_assign_rhs1 (assign_stmt
);
1686 if (TREE_CODE (TREE_OPERAND (addr_expr
, 0)) != MEM_REF
)
1687 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
, m_non_heap
);
1692 /* Handle dereferences. */
1693 for (unsigned i
= 0; i
< gimple_num_ops (stmt
); i
++)
1695 tree op
= gimple_op (stmt
, i
);
1698 if (TREE_CODE (op
) == COMPONENT_REF
)
1699 op
= TREE_OPERAND (op
, 0);
1701 if (TREE_CODE (op
) == MEM_REF
)
1703 tree arg
= TREE_OPERAND (op
, 0);
1705 state_t state
= sm_ctxt
->get_state (stmt
, arg
);
1706 if (unchecked_p (state
))
1708 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1709 sm_ctxt
->warn (node
, stmt
, arg
,
1710 new possible_null_deref (*this, diag_arg
));
1711 const allocation_state
*astate
= as_a_allocation_state (state
);
1712 sm_ctxt
->set_next_state (stmt
, arg
, astate
->get_nonnull ());
1714 else if (state
== m_null
)
1716 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1717 sm_ctxt
->warn (node
, stmt
, arg
,
1718 new null_deref (*this, diag_arg
));
1719 sm_ctxt
->set_next_state (stmt
, arg
, m_stop
);
1721 else if (freed_p (state
))
1723 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1724 const allocation_state
*astate
= as_a_allocation_state (state
);
1725 sm_ctxt
->warn (node
, stmt
, arg
,
1726 new use_after_free (*this, diag_arg
,
1727 astate
->m_deallocator
));
1728 sm_ctxt
->set_next_state (stmt
, arg
, m_stop
);
1735 /* Handle a call to an allocator.
1736 RETURNS_NONNULL is true if CALL is to a fndecl known to have
1737 __attribute__((returns_nonnull)). */
1740 malloc_state_machine::on_allocator_call (sm_context
*sm_ctxt
,
1742 const deallocator_set
*deallocators
,
1743 bool returns_nonnull
) const
1745 tree lhs
= gimple_call_lhs (call
);
1748 if (sm_ctxt
->get_state (call
, lhs
) == m_start
)
1749 sm_ctxt
->set_next_state (call
, lhs
,
1751 ? deallocators
->m_nonnull
1752 : deallocators
->m_unchecked
));
1756 /* TODO: report leak. */
1761 malloc_state_machine::on_deallocator_call (sm_context
*sm_ctxt
,
1762 const supernode
*node
,
1764 const deallocator
*d
,
1765 unsigned argno
) const
1767 if (argno
>= gimple_call_num_args (call
))
1769 tree arg
= gimple_call_arg (call
, argno
);
1771 state_t state
= sm_ctxt
->get_state (call
, arg
);
1773 /* start/unchecked/nonnull -> freed. */
1774 if (state
== m_start
)
1775 sm_ctxt
->set_next_state (call
, arg
, d
->m_freed
);
1776 else if (unchecked_p (state
) || nonnull_p (state
))
1778 const allocation_state
*astate
= as_a_allocation_state (state
);
1779 gcc_assert (astate
->m_deallocators
);
1780 if (!astate
->m_deallocators
->contains_p (d
))
1782 /* Wrong allocator. */
1783 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1784 pending_diagnostic
*pd
1785 = new mismatching_deallocation (*this, diag_arg
,
1786 astate
->m_deallocators
,
1788 sm_ctxt
->warn (node
, call
, arg
, pd
);
1790 sm_ctxt
->set_next_state (call
, arg
, d
->m_freed
);
1793 /* Keep state "null" as-is, rather than transitioning to "freed";
1794 we don't want to complain about double-free of NULL. */
1795 else if (state
== d
->m_freed
)
1797 /* freed -> stop, with warning. */
1798 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1799 sm_ctxt
->warn (node
, call
, arg
,
1800 new double_free (*this, diag_arg
, d
->m_name
));
1801 sm_ctxt
->set_next_state (call
, arg
, m_stop
);
1803 else if (state
== m_non_heap
)
1805 /* non-heap -> stop, with warning. */
1806 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1807 sm_ctxt
->warn (node
, call
, arg
,
1808 new free_of_non_heap (*this, diag_arg
,
1810 sm_ctxt
->set_next_state (call
, arg
, m_stop
);
1814 /* Implementation of realloc(3):
1816 void *realloc(void *ptr, size_t size);
1818 realloc(3) is awkward.
1820 We currently don't have a way to express multiple possible outcomes
1821 from a function call, "bifurcating" the state such as:
1822 - success: non-NULL is returned
1823 - failure: NULL is returned, existing buffer is not freed.
1824 or even an N-way state split e.g.:
1825 - buffer grew successfully in-place
1826 - buffer was successfully moved to a larger allocation
1827 - buffer was successfully contracted
1828 - realloc failed, returning NULL, without freeing existing buffer.
1829 (PR analyzer/99260 tracks this)
1831 Given that we can currently only express one outcome, eliminate
1832 false positives by dropping state from the buffer. */
1835 malloc_state_machine::on_realloc_call (sm_context
*sm_ctxt
,
1836 const supernode
*node ATTRIBUTE_UNUSED
,
1837 const gcall
*call
) const
1839 tree ptr
= gimple_call_arg (call
, 0);
1841 state_t state
= sm_ctxt
->get_state (call
, ptr
);
1843 /* Detect mismatches. */
1844 if (unchecked_p (state
) || nonnull_p (state
))
1846 const allocation_state
*astate
= as_a_allocation_state (state
);
1847 gcc_assert (astate
->m_deallocators
);
1848 if (astate
->m_deallocators
!= &m_free
)
1850 /* Wrong allocator. */
1851 tree diag_ptr
= sm_ctxt
->get_diagnostic_tree (ptr
);
1852 pending_diagnostic
*pd
1853 = new mismatching_deallocation (*this, diag_ptr
,
1854 astate
->m_deallocators
,
1856 sm_ctxt
->warn (node
, call
, ptr
, pd
);
1860 /* Transition ptr to "stop" state. */
1861 sm_ctxt
->set_next_state (call
, ptr
, m_stop
);
1864 /* Implementation of state_machine::on_phi vfunc for malloc_state_machine. */
1867 malloc_state_machine::on_phi (sm_context
*sm_ctxt
,
1868 const supernode
*node ATTRIBUTE_UNUSED
,
1874 tree lhs
= gimple_phi_result (phi
);
1875 on_zero_assignment (sm_ctxt
, phi
, lhs
);
1879 /* Implementation of state_machine::on_condition vfunc for malloc_state_machine.
1880 Potentially transition state 'unchecked' to 'nonnull' or to 'null'. */
1883 malloc_state_machine::on_condition (sm_context
*sm_ctxt
,
1884 const supernode
*node ATTRIBUTE_UNUSED
,
1888 const svalue
*rhs
) const
1890 if (!rhs
->all_zeroes_p ())
1893 if (!any_pointer_p (lhs
))
1895 if (!any_pointer_p (rhs
))
1900 log ("got 'ARG != 0' match");
1901 state_t s
= sm_ctxt
->get_state (stmt
, lhs
);
1902 if (unchecked_p (s
))
1904 const allocation_state
*astate
= as_a_allocation_state (s
);
1905 sm_ctxt
->set_next_state (stmt
, lhs
, astate
->get_nonnull ());
1908 else if (op
== EQ_EXPR
)
1910 log ("got 'ARG == 0' match");
1911 state_t s
= sm_ctxt
->get_state (stmt
, lhs
);
1912 if (unchecked_p (s
))
1913 sm_ctxt
->set_next_state (stmt
, lhs
, m_null
);
1917 /* Implementation of state_machine::can_purge_p vfunc for malloc_state_machine.
1918 Don't allow purging of pointers in state 'unchecked' or 'nonnull'
1919 (to avoid false leak reports). */
1922 malloc_state_machine::can_purge_p (state_t s
) const
1924 enum resource_state rs
= get_rs (s
);
1925 return rs
!= RS_UNCHECKED
&& rs
!= RS_NONNULL
;
1928 /* Implementation of state_machine::on_leak vfunc for malloc_state_machine
1929 (for complaining about leaks of pointers in state 'unchecked' and
1932 pending_diagnostic
*
1933 malloc_state_machine::on_leak (tree var
) const
1935 return new malloc_leak (*this, var
);
1938 /* Implementation of state_machine::reset_when_passed_to_unknown_fn_p vfunc
1939 for malloc_state_machine. */
1942 malloc_state_machine::reset_when_passed_to_unknown_fn_p (state_t s
,
1943 bool is_mutable
) const
1945 /* An on-stack ptr doesn't stop being stack-allocated when passed to an
1947 if (s
== m_non_heap
)
1950 /* Otherwise, pointers passed as non-const can be freed. */
1954 /* Return true if calls to FNDECL are known to not affect this sm-state. */
1957 malloc_state_machine::unaffected_by_call_p (tree fndecl
)
1959 /* A set of functions that are known to not affect allocation
1960 status, even if we haven't fully modelled the rest of their
1962 static const char * const funcnames
[] = {
1963 /* This array must be kept sorted. */
1967 = sizeof(funcnames
) / sizeof (funcnames
[0]);
1968 function_set
fs (funcnames
, count
);
1970 if (fs
.contains_decl_p (fndecl
))
1976 /* Shared logic for handling GIMPLE_ASSIGNs and GIMPLE_PHIs that
1977 assign zero to LHS. */
1980 malloc_state_machine::on_zero_assignment (sm_context
*sm_ctxt
,
1984 state_t s
= sm_ctxt
->get_state (stmt
, lhs
);
1985 enum resource_state rs
= get_rs (s
);
1987 || rs
== RS_UNCHECKED
1990 sm_ctxt
->set_next_state (stmt
, lhs
, m_null
);
1993 } // anonymous namespace
1995 /* Internal interface to this file. */
1998 make_malloc_state_machine (logger
*logger
)
2000 return new malloc_state_machine (logger
);
2005 #endif /* #if ENABLE_ANALYZER */