1 /* A state machine for detecting misuses of the malloc/free API.
2 Copyright (C) 2019-2022 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
12 GCC is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
23 #include "coretypes.h"
26 #include "basic-block.h"
30 #include "diagnostic-path.h"
31 #include "diagnostic-metadata.h"
34 #include "analyzer/analyzer.h"
35 #include "diagnostic-event-id.h"
36 #include "analyzer/analyzer-logging.h"
37 #include "analyzer/sm.h"
38 #include "analyzer/pending-diagnostic.h"
41 #include "analyzer/call-string.h"
42 #include "analyzer/program-point.h"
43 #include "analyzer/store.h"
44 #include "analyzer/region-model.h"
45 #include "stringpool.h"
47 #include "analyzer/function-set.h"
48 #include "analyzer/program-state.h"
56 /* This state machine and its various support classes track allocations
59 It has a few standard allocation/deallocation pairs (e.g. new/delete),
60 and also supports user-defined ones via
61 __attribute__ ((malloc(DEALLOCATOR))).
63 There can be more than one valid deallocator for a given allocator,
65 __attribute__ ((malloc (fclose)))
66 __attribute__ ((malloc (freopen, 3)))
67 FILE* fopen (const char*, const char*);
68 A deallocator_set represents a particular set of valid deallocators.
70 We track the expected deallocator_set for a value, but not the allocation
71 function - there could be more than one allocator per deallocator_set.
72 For example, there could be dozens of allocators for "free" beyond just
73 malloc e.g. calloc, xstrdup, etc. We don't want to explode the number
74 of states by tracking individual allocators in the exploded graph;
75 we merely want to track "this value expects to have 'free' called on it".
76 Perhaps we can reconstruct which allocator was used later, when emitting
77 the path, if it's necessary for precision of wording of diagnostics. */
80 class deallocator_set
;
81 class malloc_state_machine
;
83 /* An enum for discriminating between different kinds of allocation_state. */
87 /* States that are independent of allocator/deallocator. */
89 /* The start state. */
92 /* State for a pointer that's known to be NULL. */
95 /* State for a pointer that's known to not be on the heap (e.g. to a local
99 /* Stop state, for pointers we don't want to track any more. */
102 /* States that relate to a specific deallocator_set. */
104 /* State for a pointer returned from an allocator that hasn't
105 been checked for NULL.
106 It could be a pointer to heap-allocated memory, or could be NULL. */
109 /* State for a pointer returned from an allocator,
110 known to be non-NULL. */
113 /* State for a pointer passed to a deallocator. */
117 /* Custom state subclass, which can optionally refer to an a
120 struct allocation_state
: public state_machine::state
122 allocation_state (const char *name
, unsigned id
,
123 enum resource_state rs
,
124 const deallocator_set
*deallocators
,
125 const deallocator
*deallocator
)
126 : state (name
, id
), m_rs (rs
),
127 m_deallocators (deallocators
),
128 m_deallocator (deallocator
)
131 void dump_to_pp (pretty_printer
*pp
) const FINAL OVERRIDE
;
133 const allocation_state
*get_nonnull () const;
135 enum resource_state m_rs
;
136 const deallocator_set
*m_deallocators
;
137 const deallocator
*m_deallocator
;
140 /* An enum for choosing which wording to use in various diagnostics
141 when describing deallocations. */
151 /* Base class representing a deallocation function,
152 either a built-in one we know about, or one exposed via
153 __attribute__((malloc(DEALLOCATOR))). */
157 hashval_t
hash () const;
158 void dump_to_pp (pretty_printer
*pp
) const;
159 static int cmp (const deallocator
*a
, const deallocator
*b
);
160 static int cmp_ptr_ptr (const void *, const void *);
162 /* Name to use in diagnostics. */
165 /* Which wording to use in diagnostics. */
166 enum wording m_wording
;
168 /* State for a value passed to one of the deallocators. */
169 state_machine::state_t m_freed
;
172 deallocator (malloc_state_machine
*sm
,
174 enum wording wording
);
177 /* Subclass representing a predefined deallocator.
178 e.g. "delete []", without needing a specific FUNCTION_DECL
181 struct standard_deallocator
: public deallocator
183 standard_deallocator (malloc_state_machine
*sm
,
185 enum wording wording
);
188 /* Subclass representing a user-defined deallocator
189 via __attribute__((malloc(DEALLOCATOR))) given
190 a specific FUNCTION_DECL. */
192 struct custom_deallocator
: public deallocator
194 custom_deallocator (malloc_state_machine
*sm
,
195 tree deallocator_fndecl
,
196 enum wording wording
)
197 : deallocator (sm
, IDENTIFIER_POINTER (DECL_NAME (deallocator_fndecl
)),
203 /* Base class representing a set of possible deallocators.
204 Often this will be just a single deallocator, but some
205 allocators have multiple valid deallocators (e.g. the result of
206 "fopen" can be closed by either "fclose" or "freopen"). */
208 struct deallocator_set
210 deallocator_set (malloc_state_machine
*sm
,
211 enum wording wording
);
212 virtual ~deallocator_set () {}
214 virtual bool contains_p (const deallocator
*d
) const = 0;
215 virtual const deallocator
*maybe_get_single () const = 0;
216 virtual void dump_to_pp (pretty_printer
*pp
) const = 0;
219 /* Which wording to use in diagnostics. */
220 enum wording m_wording
;
222 /* Pointers to states.
223 These states are owned by the state_machine base class. */
225 /* State for an unchecked result from an allocator using this set. */
226 state_machine::state_t m_unchecked
;
228 /* State for a known non-NULL result from such an allocator. */
229 state_machine::state_t m_nonnull
;
232 /* Subclass of deallocator_set representing a set of deallocators
233 defined by one or more __attribute__((malloc(DEALLOCATOR))). */
235 struct custom_deallocator_set
: public deallocator_set
237 typedef const auto_vec
<const deallocator
*> *key_t
;
239 custom_deallocator_set (malloc_state_machine
*sm
,
240 const auto_vec
<const deallocator
*> *vec
,
242 //const char *dealloc_funcname,
244 enum wording wording
);
246 bool contains_p (const deallocator
*d
) const FINAL OVERRIDE
;
247 const deallocator
*maybe_get_single () const FINAL OVERRIDE
;
248 void dump_to_pp (pretty_printer
*pp
) const FINAL OVERRIDE
;
250 auto_vec
<const deallocator
*> m_deallocator_vec
;
253 /* Subclass of deallocator_set representing a set of deallocators
254 with a single standard_deallocator, e.g. "delete []". */
256 struct standard_deallocator_set
: public deallocator_set
258 standard_deallocator_set (malloc_state_machine
*sm
,
260 enum wording wording
);
262 bool contains_p (const deallocator
*d
) const FINAL OVERRIDE
;
263 const deallocator
*maybe_get_single () const FINAL OVERRIDE
;
264 void dump_to_pp (pretty_printer
*pp
) const FINAL OVERRIDE
;
266 standard_deallocator m_deallocator
;
269 /* Traits class for ensuring uniqueness of deallocator_sets within
270 malloc_state_machine. */
272 struct deallocator_set_map_traits
274 typedef custom_deallocator_set::key_t key_type
;
275 typedef custom_deallocator_set
*value_type
;
276 typedef custom_deallocator_set
*compare_type
;
278 static inline hashval_t
hash (const key_type
&k
)
280 gcc_assert (k
!= NULL
);
281 gcc_assert (k
!= reinterpret_cast<key_type
> (1));
283 hashval_t result
= 0;
285 const deallocator
*d
;
286 FOR_EACH_VEC_ELT (*k
, i
, d
)
287 result
^= d
->hash ();
290 static inline bool equal_keys (const key_type
&k1
, const key_type
&k2
)
292 if (k1
->length () != k2
->length ())
295 for (unsigned i
= 0; i
< k1
->length (); i
++)
296 if ((*k1
)[i
] != (*k2
)[i
])
301 template <typename T
>
302 static inline void remove (T
&)
304 /* empty; the nodes are handled elsewhere. */
306 template <typename T
>
307 static inline void mark_deleted (T
&entry
)
309 entry
.m_key
= reinterpret_cast<key_type
> (1);
311 template <typename T
>
312 static inline void mark_empty (T
&entry
)
316 template <typename T
>
317 static inline bool is_deleted (const T
&entry
)
319 return entry
.m_key
== reinterpret_cast<key_type
> (1);
321 template <typename T
>
322 static inline bool is_empty (const T
&entry
)
324 return entry
.m_key
== NULL
;
326 static const bool empty_zero_p
= false;
329 /* A state machine for detecting misuses of the malloc/free API.
331 See sm-malloc.dot for an overview (keep this in-sync with that file). */
333 class malloc_state_machine
: public state_machine
336 typedef allocation_state custom_data_t
;
338 malloc_state_machine (logger
*logger
);
339 ~malloc_state_machine ();
342 add_state (const char *name
, enum resource_state rs
,
343 const deallocator_set
*deallocators
,
344 const deallocator
*deallocator
);
346 bool inherited_state_p () const FINAL OVERRIDE
{ return false; }
348 state_machine::state_t
349 get_default_state (const svalue
*sval
) const FINAL OVERRIDE
351 if (tree cst
= sval
->maybe_get_constant ())
356 if (const region_svalue
*ptr
= sval
->dyn_cast_region_svalue ())
358 const region
*reg
= ptr
->get_pointee ();
359 const region
*base_reg
= reg
->get_base_region ();
360 if (base_reg
->get_kind () == RK_DECL
361 || base_reg
->get_kind () == RK_STRING
)
367 bool on_stmt (sm_context
*sm_ctxt
,
368 const supernode
*node
,
369 const gimple
*stmt
) const FINAL OVERRIDE
;
371 void on_phi (sm_context
*sm_ctxt
,
372 const supernode
*node
,
374 tree rhs
) const FINAL OVERRIDE
;
376 void on_condition (sm_context
*sm_ctxt
,
377 const supernode
*node
,
381 const svalue
*rhs
) const FINAL OVERRIDE
;
383 bool can_purge_p (state_t s
) const FINAL OVERRIDE
;
384 pending_diagnostic
*on_leak (tree var
) const FINAL OVERRIDE
;
386 bool reset_when_passed_to_unknown_fn_p (state_t s
,
387 bool is_mutable
) const FINAL OVERRIDE
;
389 static bool unaffected_by_call_p (tree fndecl
);
391 void on_realloc_with_move (region_model
*model
,
393 const svalue
*old_ptr_sval
,
394 const svalue
*new_ptr_sval
,
395 const extrinsic_state
&ext_state
) const;
397 standard_deallocator_set m_free
;
398 standard_deallocator_set m_scalar_delete
;
399 standard_deallocator_set m_vector_delete
;
401 standard_deallocator m_realloc
;
403 /* States that are independent of api. */
405 /* State for a pointer that's known to be NULL. */
408 /* State for a pointer that's known to not be on the heap (e.g. to a local
410 state_t m_non_heap
; // TODO: or should this be a different state machine?
411 // or do we need child values etc?
413 /* Stop state, for pointers we don't want to track any more. */
417 const custom_deallocator_set
*
418 get_or_create_custom_deallocator_set (tree allocator_fndecl
);
419 custom_deallocator_set
*
420 maybe_create_custom_deallocator_set (tree allocator_fndecl
);
422 get_or_create_deallocator (tree deallocator_fndecl
);
424 void on_allocator_call (sm_context
*sm_ctxt
,
426 const deallocator_set
*deallocators
,
427 bool returns_nonnull
= false) const;
428 void on_deallocator_call (sm_context
*sm_ctxt
,
429 const supernode
*node
,
431 const deallocator
*d
,
432 unsigned argno
) const;
433 void on_realloc_call (sm_context
*sm_ctxt
,
434 const supernode
*node
,
435 const gcall
*call
) const;
436 void on_zero_assignment (sm_context
*sm_ctxt
,
440 /* A map for consolidating deallocators so that they are
441 unique per deallocator FUNCTION_DECL. */
442 typedef hash_map
<tree
, deallocator
*> deallocator_map_t
;
443 deallocator_map_t m_deallocator_map
;
445 /* Memoized lookups from FUNCTION_DECL to custom_deallocator_set *. */
446 typedef hash_map
<tree
, custom_deallocator_set
*> deallocator_set_cache_t
;
447 deallocator_set_cache_t m_custom_deallocator_set_cache
;
449 /* A map for consolidating custom_deallocator_set instances. */
450 typedef hash_map
<custom_deallocator_set::key_t
,
451 custom_deallocator_set
*,
452 deallocator_set_map_traits
> custom_deallocator_set_map_t
;
453 custom_deallocator_set_map_t m_custom_deallocator_set_map
;
455 /* Record of dynamically-allocated objects, for cleanup. */
456 auto_vec
<custom_deallocator_set
*> m_dynamic_sets
;
457 auto_vec
<custom_deallocator
*> m_dynamic_deallocators
;
460 /* struct deallocator. */
462 deallocator::deallocator (malloc_state_machine
*sm
,
464 enum wording wording
)
467 m_freed (sm
->add_state ("freed", RS_FREED
, NULL
, this))
472 deallocator::hash () const
474 return (hashval_t
)m_freed
->get_id ();
478 deallocator::dump_to_pp (pretty_printer
*pp
) const
480 pp_printf (pp
, "%qs", m_name
);
484 deallocator::cmp (const deallocator
*a
, const deallocator
*b
)
486 return (int)a
->m_freed
->get_id () - (int)b
->m_freed
->get_id ();
490 deallocator::cmp_ptr_ptr (const void *a
, const void *b
)
492 return cmp (*(const deallocator
* const *)a
,
493 *(const deallocator
* const *)b
);
497 /* struct standard_deallocator : public deallocator. */
499 standard_deallocator::standard_deallocator (malloc_state_machine
*sm
,
501 enum wording wording
)
502 : deallocator (sm
, name
, wording
)
506 /* struct deallocator_set. */
508 deallocator_set::deallocator_set (malloc_state_machine
*sm
,
509 enum wording wording
)
510 : m_wording (wording
),
511 m_unchecked (sm
->add_state ("unchecked", RS_UNCHECKED
, this, NULL
)),
512 m_nonnull (sm
->add_state ("nonnull", RS_NONNULL
, this, NULL
))
516 /* Dump a description of this deallocator_set to stderr. */
519 deallocator_set::dump () const
522 pp_show_color (&pp
) = pp_show_color (global_dc
->printer
);
523 pp
.buffer
->stream
= stderr
;
529 /* struct custom_deallocator_set : public deallocator_set. */
531 custom_deallocator_set::
532 custom_deallocator_set (malloc_state_machine
*sm
,
533 const auto_vec
<const deallocator
*> *vec
,
534 enum wording wording
)
535 : deallocator_set (sm
, wording
),
536 m_deallocator_vec (vec
->length ())
539 const deallocator
*d
;
540 FOR_EACH_VEC_ELT (*vec
, i
, d
)
541 m_deallocator_vec
.safe_push (d
);
545 custom_deallocator_set::contains_p (const deallocator
*d
) const
548 const deallocator
*cd
;
549 FOR_EACH_VEC_ELT (m_deallocator_vec
, i
, cd
)
556 custom_deallocator_set::maybe_get_single () const
558 if (m_deallocator_vec
.length () == 1)
559 return m_deallocator_vec
[0];
564 custom_deallocator_set::dump_to_pp (pretty_printer
*pp
) const
566 pp_character (pp
, '{');
568 const deallocator
*d
;
569 FOR_EACH_VEC_ELT (m_deallocator_vec
, i
, d
)
572 pp_string (pp
, ", ");
575 pp_character (pp
, '}');
578 /* struct standard_deallocator_set : public deallocator_set. */
580 standard_deallocator_set::standard_deallocator_set (malloc_state_machine
*sm
,
582 enum wording wording
)
583 : deallocator_set (sm
, wording
),
584 m_deallocator (sm
, name
, wording
)
589 standard_deallocator_set::contains_p (const deallocator
*d
) const
591 return d
== &m_deallocator
;
595 standard_deallocator_set::maybe_get_single () const
597 return &m_deallocator
;
601 standard_deallocator_set::dump_to_pp (pretty_printer
*pp
) const
603 pp_character (pp
, '{');
604 pp_string (pp
, m_deallocator
.m_name
);
605 pp_character (pp
, '}');
608 /* Return STATE cast to the custom state subclass, or NULL for the start state.
609 Everything should be an allocation_state apart from the start state. */
611 static const allocation_state
*
612 dyn_cast_allocation_state (state_machine::state_t state
)
614 if (state
->get_id () == 0)
616 return static_cast <const allocation_state
*> (state
);
619 /* Return STATE cast to the custom state subclass, for a state that is
620 already known to not be the start state . */
622 static const allocation_state
*
623 as_a_allocation_state (state_machine::state_t state
)
625 gcc_assert (state
->get_id () != 0);
626 return static_cast <const allocation_state
*> (state
);
629 /* Get the resource_state for STATE. */
631 static enum resource_state
632 get_rs (state_machine::state_t state
)
634 if (const allocation_state
*astate
= dyn_cast_allocation_state (state
))
640 /* Return true if STATE is the start state. */
643 start_p (state_machine::state_t state
)
645 return get_rs (state
) == RS_START
;
648 /* Return true if STATE is an unchecked result from an allocator. */
651 unchecked_p (state_machine::state_t state
)
653 return get_rs (state
) == RS_UNCHECKED
;
656 /* Return true if STATE is a non-null result from an allocator. */
659 nonnull_p (state_machine::state_t state
)
661 return get_rs (state
) == RS_NONNULL
;
664 /* Return true if STATE is a value that has been passed to a deallocator. */
667 freed_p (state_machine::state_t state
)
669 return get_rs (state
) == RS_FREED
;
672 /* Class for diagnostics relating to malloc_state_machine. */
674 class malloc_diagnostic
: public pending_diagnostic
677 malloc_diagnostic (const malloc_state_machine
&sm
, tree arg
)
678 : m_sm (sm
), m_arg (arg
)
681 bool subclass_equal_p (const pending_diagnostic
&base_other
) const OVERRIDE
683 return same_tree_p (m_arg
, ((const malloc_diagnostic
&)base_other
).m_arg
);
686 label_text
describe_state_change (const evdesc::state_change
&change
)
689 if (change
.m_old_state
== m_sm
.get_start_state ()
690 && unchecked_p (change
.m_new_state
))
691 // TODO: verify that it's the allocation stmt, not a copy
692 return label_text::borrow ("allocated here");
693 if (unchecked_p (change
.m_old_state
)
694 && nonnull_p (change
.m_new_state
))
697 return change
.formatted_print ("assuming %qE is non-NULL",
700 return change
.formatted_print ("assuming %qs is non-NULL",
703 if (change
.m_new_state
== m_sm
.m_null
)
705 if (unchecked_p (change
.m_old_state
))
708 return change
.formatted_print ("assuming %qE is NULL",
711 return change
.formatted_print ("assuming %qs is NULL",
717 return change
.formatted_print ("%qE is NULL",
720 return change
.formatted_print ("%qs is NULL",
725 return label_text ();
729 const malloc_state_machine
&m_sm
;
733 /* Concrete subclass for reporting mismatching allocator/deallocator
736 class mismatching_deallocation
: public malloc_diagnostic
739 mismatching_deallocation (const malloc_state_machine
&sm
, tree arg
,
740 const deallocator_set
*expected_deallocators
,
741 const deallocator
*actual_dealloc
)
742 : malloc_diagnostic (sm
, arg
),
743 m_expected_deallocators (expected_deallocators
),
744 m_actual_dealloc (actual_dealloc
)
747 const char *get_kind () const FINAL OVERRIDE
749 return "mismatching_deallocation";
752 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
754 auto_diagnostic_group d
;
755 diagnostic_metadata m
;
756 m
.add_cwe (762); /* CWE-762: Mismatched Memory Management Routines. */
757 if (const deallocator
*expected_dealloc
758 = m_expected_deallocators
->maybe_get_single ())
759 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_mismatching_deallocation
,
760 "%qE should have been deallocated with %qs"
761 " but was deallocated with %qs",
762 m_arg
, expected_dealloc
->m_name
,
763 m_actual_dealloc
->m_name
);
765 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_mismatching_deallocation
,
766 "%qs called on %qE returned from a mismatched"
767 " allocation function",
768 m_actual_dealloc
->m_name
, m_arg
);
771 label_text
describe_state_change (const evdesc::state_change
&change
)
774 if (unchecked_p (change
.m_new_state
))
776 m_alloc_event
= change
.m_event_id
;
777 if (const deallocator
*expected_dealloc
778 = m_expected_deallocators
->maybe_get_single ())
779 return change
.formatted_print ("allocated here"
780 " (expects deallocation with %qs)",
781 expected_dealloc
->m_name
);
783 return change
.formatted_print ("allocated here");
785 return malloc_diagnostic::describe_state_change (change
);
788 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
790 if (m_alloc_event
.known_p ())
792 if (const deallocator
*expected_dealloc
793 = m_expected_deallocators
->maybe_get_single ())
794 return ev
.formatted_print
795 ("deallocated with %qs here;"
796 " allocation at %@ expects deallocation with %qs",
797 m_actual_dealloc
->m_name
, &m_alloc_event
,
798 expected_dealloc
->m_name
);
800 return ev
.formatted_print
801 ("deallocated with %qs here;"
803 m_actual_dealloc
->m_name
, &m_alloc_event
);
805 return ev
.formatted_print ("deallocated with %qs here",
806 m_actual_dealloc
->m_name
);
810 diagnostic_event_id_t m_alloc_event
;
811 const deallocator_set
*m_expected_deallocators
;
812 const deallocator
*m_actual_dealloc
;
815 /* Concrete subclass for reporting double-free diagnostics. */
817 class double_free
: public malloc_diagnostic
820 double_free (const malloc_state_machine
&sm
, tree arg
, const char *funcname
)
821 : malloc_diagnostic (sm
, arg
), m_funcname (funcname
)
824 const char *get_kind () const FINAL OVERRIDE
{ return "double_free"; }
826 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
828 auto_diagnostic_group d
;
829 diagnostic_metadata m
;
830 m
.add_cwe (415); /* CWE-415: Double Free. */
831 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_double_free
,
832 "double-%qs of %qE", m_funcname
, m_arg
);
835 label_text
describe_state_change (const evdesc::state_change
&change
)
838 if (freed_p (change
.m_new_state
))
840 m_first_free_event
= change
.m_event_id
;
841 return change
.formatted_print ("first %qs here", m_funcname
);
843 return malloc_diagnostic::describe_state_change (change
);
846 label_text
describe_call_with_state (const evdesc::call_with_state
&info
)
849 if (freed_p (info
.m_state
))
850 return info
.formatted_print
851 ("passing freed pointer %qE in call to %qE from %qE",
852 info
.m_expr
, info
.m_callee_fndecl
, info
.m_caller_fndecl
);
853 return label_text ();
856 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
858 if (m_first_free_event
.known_p ())
859 return ev
.formatted_print ("second %qs here; first %qs was at %@",
860 m_funcname
, m_funcname
,
861 &m_first_free_event
);
862 return ev
.formatted_print ("second %qs here", m_funcname
);
866 diagnostic_event_id_t m_first_free_event
;
867 const char *m_funcname
;
870 /* Abstract subclass for describing possible bad uses of NULL.
871 Responsible for describing the call that could return NULL. */
873 class possible_null
: public malloc_diagnostic
876 possible_null (const malloc_state_machine
&sm
, tree arg
)
877 : malloc_diagnostic (sm
, arg
)
880 label_text
describe_state_change (const evdesc::state_change
&change
)
883 if (change
.m_old_state
== m_sm
.get_start_state ()
884 && unchecked_p (change
.m_new_state
))
886 m_origin_of_unchecked_event
= change
.m_event_id
;
887 return label_text::borrow ("this call could return NULL");
889 return malloc_diagnostic::describe_state_change (change
);
892 label_text
describe_return_of_state (const evdesc::return_of_state
&info
)
895 if (unchecked_p (info
.m_state
))
896 return info
.formatted_print ("possible return of NULL to %qE from %qE",
897 info
.m_caller_fndecl
, info
.m_callee_fndecl
);
898 return label_text ();
902 diagnostic_event_id_t m_origin_of_unchecked_event
;
905 /* Concrete subclass for describing dereference of a possible NULL
908 class possible_null_deref
: public possible_null
911 possible_null_deref (const malloc_state_machine
&sm
, tree arg
)
912 : possible_null (sm
, arg
)
915 const char *get_kind () const FINAL OVERRIDE
{ return "possible_null_deref"; }
917 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
919 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
920 diagnostic_metadata m
;
922 return warning_meta (rich_loc
, m
,
923 OPT_Wanalyzer_possible_null_dereference
,
924 "dereference of possibly-NULL %qE", m_arg
);
927 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
929 if (m_origin_of_unchecked_event
.known_p ())
930 return ev
.formatted_print ("%qE could be NULL: unchecked value from %@",
932 &m_origin_of_unchecked_event
);
934 return ev
.formatted_print ("%qE could be NULL", ev
.m_expr
);
939 /* Return true if FNDECL is a C++ method. */
942 method_p (tree fndecl
)
944 return TREE_CODE (TREE_TYPE (fndecl
)) == METHOD_TYPE
;
947 /* Return a 1-based description of ARG_IDX (0-based) of FNDECL.
948 Compare with %P in the C++ FE (implemented in cp/error.cc: parm_to_string
949 as called from cp_printer). */
952 describe_argument_index (tree fndecl
, int arg_idx
)
954 if (method_p (fndecl
))
956 return label_text::borrow ("'this'");
958 pp_printf (&pp
, "%u", arg_idx
+ 1 - method_p (fndecl
));
959 return label_text::take (xstrdup (pp_formatted_text (&pp
)));
962 /* Subroutine for use by possible_null_arg::emit and null_arg::emit.
963 Issue a note informing that the pertinent argument must be non-NULL. */
966 inform_nonnull_attribute (tree fndecl
, int arg_idx
)
968 label_text arg_desc
= describe_argument_index (fndecl
, arg_idx
);
969 inform (DECL_SOURCE_LOCATION (fndecl
),
970 "argument %s of %qD must be non-null",
971 arg_desc
.m_buffer
, fndecl
);
972 arg_desc
.maybe_free ();
973 /* Ideally we would use the location of the parm and underline the
974 attribute also - but we don't have the location_t values at this point
976 For reference, the C and C++ FEs have get_fndecl_argument_location. */
979 /* Concrete subclass for describing passing a possibly-NULL value to a
980 function marked with __attribute__((nonnull)). */
982 class possible_null_arg
: public possible_null
985 possible_null_arg (const malloc_state_machine
&sm
, tree arg
,
986 tree fndecl
, int arg_idx
)
987 : possible_null (sm
, arg
),
988 m_fndecl (fndecl
), m_arg_idx (arg_idx
)
991 const char *get_kind () const FINAL OVERRIDE
{ return "possible_null_arg"; }
993 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
995 const possible_null_arg
&sub_other
996 = (const possible_null_arg
&)base_other
;
997 return (same_tree_p (m_arg
, sub_other
.m_arg
)
998 && m_fndecl
== sub_other
.m_fndecl
999 && m_arg_idx
== sub_other
.m_arg_idx
);
1003 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
1005 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
1006 auto_diagnostic_group d
;
1007 diagnostic_metadata m
;
1010 = warning_meta (rich_loc
, m
, OPT_Wanalyzer_possible_null_argument
,
1011 "use of possibly-NULL %qE where non-null expected",
1014 inform_nonnull_attribute (m_fndecl
, m_arg_idx
);
1018 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
1020 label_text arg_desc
= describe_argument_index (m_fndecl
, m_arg_idx
);
1022 if (m_origin_of_unchecked_event
.known_p ())
1023 result
= ev
.formatted_print ("argument %s (%qE) from %@ could be NULL"
1024 " where non-null expected",
1025 arg_desc
.m_buffer
, ev
.m_expr
,
1026 &m_origin_of_unchecked_event
);
1028 result
= ev
.formatted_print ("argument %s (%qE) could be NULL"
1029 " where non-null expected",
1030 arg_desc
.m_buffer
, ev
.m_expr
);
1031 arg_desc
.maybe_free ();
1040 /* Concrete subclass for describing a dereference of a NULL value. */
1042 class null_deref
: public malloc_diagnostic
1045 null_deref (const malloc_state_machine
&sm
, tree arg
)
1046 : malloc_diagnostic (sm
, arg
) {}
1048 const char *get_kind () const FINAL OVERRIDE
{ return "null_deref"; }
1050 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
1052 /* CWE-476: NULL Pointer Dereference. */
1053 diagnostic_metadata m
;
1055 return warning_meta (rich_loc
, m
,
1056 OPT_Wanalyzer_null_dereference
,
1057 "dereference of NULL %qE", m_arg
);
1060 label_text
describe_return_of_state (const evdesc::return_of_state
&info
)
1063 if (info
.m_state
== m_sm
.m_null
)
1064 return info
.formatted_print ("return of NULL to %qE from %qE",
1065 info
.m_caller_fndecl
, info
.m_callee_fndecl
);
1066 return label_text ();
1069 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
1071 return ev
.formatted_print ("dereference of NULL %qE", ev
.m_expr
);
1075 /* Concrete subclass for describing passing a NULL value to a
1076 function marked with __attribute__((nonnull)). */
1078 class null_arg
: public malloc_diagnostic
1081 null_arg (const malloc_state_machine
&sm
, tree arg
,
1082 tree fndecl
, int arg_idx
)
1083 : malloc_diagnostic (sm
, arg
),
1084 m_fndecl (fndecl
), m_arg_idx (arg_idx
)
1087 const char *get_kind () const FINAL OVERRIDE
{ return "null_arg"; }
1089 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
1091 const null_arg
&sub_other
1092 = (const null_arg
&)base_other
;
1093 return (same_tree_p (m_arg
, sub_other
.m_arg
)
1094 && m_fndecl
== sub_other
.m_fndecl
1095 && m_arg_idx
== sub_other
.m_arg_idx
);
1098 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
1100 /* CWE-476: NULL Pointer Dereference. */
1101 auto_diagnostic_group d
;
1102 diagnostic_metadata m
;
1107 warned
= warning_meta (rich_loc
, m
, OPT_Wanalyzer_null_argument
,
1108 "use of NULL where non-null expected");
1110 warned
= warning_meta (rich_loc
, m
, OPT_Wanalyzer_null_argument
,
1111 "use of NULL %qE where non-null expected",
1114 inform_nonnull_attribute (m_fndecl
, m_arg_idx
);
1118 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
1120 label_text arg_desc
= describe_argument_index (m_fndecl
, m_arg_idx
);
1122 if (zerop (ev
.m_expr
))
1123 result
= ev
.formatted_print ("argument %s NULL where non-null expected",
1126 result
= ev
.formatted_print ("argument %s (%qE) NULL"
1127 " where non-null expected",
1128 arg_desc
.m_buffer
, ev
.m_expr
);
1129 arg_desc
.maybe_free ();
1138 class use_after_free
: public malloc_diagnostic
1141 use_after_free (const malloc_state_machine
&sm
, tree arg
,
1142 const deallocator
*deallocator
)
1143 : malloc_diagnostic (sm
, arg
),
1144 m_deallocator (deallocator
)
1146 gcc_assert (deallocator
);
1149 const char *get_kind () const FINAL OVERRIDE
{ return "use_after_free"; }
1151 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
1153 /* CWE-416: Use After Free. */
1154 diagnostic_metadata m
;
1156 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_use_after_free
,
1157 "use after %<%s%> of %qE",
1158 m_deallocator
->m_name
, m_arg
);
1161 label_text
describe_state_change (const evdesc::state_change
&change
)
1164 if (freed_p (change
.m_new_state
))
1166 m_free_event
= change
.m_event_id
;
1167 switch (m_deallocator
->m_wording
)
1170 case WORDING_REALLOCATED
:
1173 return label_text::borrow ("freed here");
1174 case WORDING_DELETED
:
1175 return label_text::borrow ("deleted here");
1176 case WORDING_DEALLOCATED
:
1177 return label_text::borrow ("deallocated here");
1180 return malloc_diagnostic::describe_state_change (change
);
1183 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
1185 const char *funcname
= m_deallocator
->m_name
;
1186 if (m_free_event
.known_p ())
1187 switch (m_deallocator
->m_wording
)
1190 case WORDING_REALLOCATED
:
1193 return ev
.formatted_print ("use after %<%s%> of %qE; freed at %@",
1194 funcname
, ev
.m_expr
, &m_free_event
);
1195 case WORDING_DELETED
:
1196 return ev
.formatted_print ("use after %<%s%> of %qE; deleted at %@",
1197 funcname
, ev
.m_expr
, &m_free_event
);
1198 case WORDING_DEALLOCATED
:
1199 return ev
.formatted_print ("use after %<%s%> of %qE;"
1200 " deallocated at %@",
1201 funcname
, ev
.m_expr
, &m_free_event
);
1204 return ev
.formatted_print ("use after %<%s%> of %qE",
1205 funcname
, ev
.m_expr
);
1208 /* Implementation of pending_diagnostic::supercedes_p for
1211 We want use-after-free to supercede use-of-unitialized-value,
1212 so that if we have these at the same stmt, we don't emit
1213 a use-of-uninitialized, just the use-after-free.
1214 (this is because we fully purge information about freed
1215 buffers when we free them to avoid state explosions, so
1216 that if they are accessed after the free, it looks like
1217 they are uninitialized). */
1219 bool supercedes_p (const pending_diagnostic
&other
) const FINAL OVERRIDE
1221 if (other
.use_of_uninit_p ())
1228 diagnostic_event_id_t m_free_event
;
1229 const deallocator
*m_deallocator
;
1232 class malloc_leak
: public malloc_diagnostic
1235 malloc_leak (const malloc_state_machine
&sm
, tree arg
)
1236 : malloc_diagnostic (sm
, arg
) {}
1238 const char *get_kind () const FINAL OVERRIDE
{ return "malloc_leak"; }
1240 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
1242 diagnostic_metadata m
;
1245 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_malloc_leak
,
1246 "leak of %qE", m_arg
);
1248 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_malloc_leak
,
1249 "leak of %qs", "<unknown>");
1252 label_text
describe_state_change (const evdesc::state_change
&change
)
1255 if (unchecked_p (change
.m_new_state
)
1256 || (start_p (change
.m_old_state
) && nonnull_p (change
.m_new_state
)))
1258 m_alloc_event
= change
.m_event_id
;
1259 return label_text::borrow ("allocated here");
1261 return malloc_diagnostic::describe_state_change (change
);
1264 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
1268 if (m_alloc_event
.known_p ())
1269 return ev
.formatted_print ("%qE leaks here; was allocated at %@",
1270 ev
.m_expr
, &m_alloc_event
);
1272 return ev
.formatted_print ("%qE leaks here", ev
.m_expr
);
1276 if (m_alloc_event
.known_p ())
1277 return ev
.formatted_print ("%qs leaks here; was allocated at %@",
1278 "<unknown>", &m_alloc_event
);
1280 return ev
.formatted_print ("%qs leaks here", "<unknown>");
1285 diagnostic_event_id_t m_alloc_event
;
1288 class free_of_non_heap
: public malloc_diagnostic
1291 free_of_non_heap (const malloc_state_machine
&sm
, tree arg
,
1292 const char *funcname
)
1293 : malloc_diagnostic (sm
, arg
), m_funcname (funcname
), m_kind (KIND_UNKNOWN
)
1297 const char *get_kind () const FINAL OVERRIDE
{ return "free_of_non_heap"; }
1299 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
1302 const free_of_non_heap
&other
= (const free_of_non_heap
&)base_other
;
1303 return (same_tree_p (m_arg
, other
.m_arg
) && m_kind
== other
.m_kind
);
1306 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
1308 auto_diagnostic_group d
;
1309 diagnostic_metadata m
;
1310 m
.add_cwe (590); /* CWE-590: Free of Memory not on the Heap. */
1316 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_free_of_non_heap
,
1317 "%<%s%> of %qE which points to memory"
1322 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_free_of_non_heap
,
1323 "%<%s%> of memory allocated on the stack by"
1324 " %qs (%qE) will corrupt the heap",
1325 m_funcname
, "alloca", m_arg
);
1330 label_text
describe_state_change (const evdesc::state_change
&change
)
1333 /* Attempt to reconstruct what kind of pointer it is.
1334 (It seems neater for this to be a part of the state, though). */
1335 if (change
.m_expr
&& TREE_CODE (change
.m_expr
) == SSA_NAME
)
1337 gimple
*def_stmt
= SSA_NAME_DEF_STMT (change
.m_expr
);
1338 if (gcall
*call
= dyn_cast
<gcall
*> (def_stmt
))
1340 if (is_special_named_call_p (call
, "alloca", 1)
1341 || is_special_named_call_p (call
, "__builtin_alloca", 1))
1343 m_kind
= KIND_ALLOCA
;
1344 return label_text::borrow
1345 ("memory is allocated on the stack here");
1349 return label_text::borrow ("pointer is from here");
1352 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
1354 return ev
.formatted_print ("call to %qs here", m_funcname
);
1363 const char *m_funcname
;
1367 /* struct allocation_state : public state_machine::state. */
1369 /* Implementation of state_machine::state::dump_to_pp vfunc
1370 for allocation_state: append the API that this allocation is
1374 allocation_state::dump_to_pp (pretty_printer
*pp
) const
1376 state_machine::state::dump_to_pp (pp
);
1379 pp_string (pp
, " (");
1380 m_deallocators
->dump_to_pp (pp
);
1381 pp_character (pp
, ')');
1385 /* Given a allocation_state for a deallocator_set, get the "nonnull" state
1386 for the corresponding allocator(s). */
1388 const allocation_state
*
1389 allocation_state::get_nonnull () const
1391 gcc_assert (m_deallocators
);
1392 return as_a_allocation_state (m_deallocators
->m_nonnull
);
1395 /* malloc_state_machine's ctor. */
1397 malloc_state_machine::malloc_state_machine (logger
*logger
)
1398 : state_machine ("malloc", logger
),
1399 m_free (this, "free", WORDING_FREED
),
1400 m_scalar_delete (this, "delete", WORDING_DELETED
),
1401 m_vector_delete (this, "delete[]", WORDING_DELETED
),
1402 m_realloc (this, "realloc", WORDING_REALLOCATED
)
1404 gcc_assert (m_start
->get_id () == 0);
1405 m_null
= add_state ("null", RS_FREED
, NULL
, NULL
);
1406 m_non_heap
= add_state ("non-heap", RS_NON_HEAP
, NULL
, NULL
);
1407 m_stop
= add_state ("stop", RS_STOP
, NULL
, NULL
);
1410 malloc_state_machine::~malloc_state_machine ()
1413 custom_deallocator_set
*set
;
1414 FOR_EACH_VEC_ELT (m_dynamic_sets
, i
, set
)
1416 custom_deallocator
*d
;
1417 FOR_EACH_VEC_ELT (m_dynamic_deallocators
, i
, d
)
1421 state_machine::state_t
1422 malloc_state_machine::add_state (const char *name
, enum resource_state rs
,
1423 const deallocator_set
*deallocators
,
1424 const deallocator
*deallocator
)
1426 return add_custom_state (new allocation_state (name
, alloc_state_id (),
1431 /* If ALLOCATOR_FNDECL has any "__attribute__((malloc(FOO)))",
1432 return a custom_deallocator_set for them, consolidating them
1433 to ensure uniqueness of the sets.
1435 Return NULL if it has no such attributes. */
1437 const custom_deallocator_set
*
1438 malloc_state_machine::
1439 get_or_create_custom_deallocator_set (tree allocator_fndecl
)
1441 /* Early rejection of decls without attributes. */
1442 tree attrs
= DECL_ATTRIBUTES (allocator_fndecl
);
1446 /* Otherwise, call maybe_create_custom_deallocator_set,
1447 memoizing the result. */
1448 if (custom_deallocator_set
**slot
1449 = m_custom_deallocator_set_cache
.get (allocator_fndecl
))
1451 custom_deallocator_set
*set
1452 = maybe_create_custom_deallocator_set (allocator_fndecl
);
1453 m_custom_deallocator_set_cache
.put (allocator_fndecl
, set
);
1457 /* Given ALLOCATOR_FNDECL, a FUNCTION_DECL with attributes,
1458 look for any "__attribute__((malloc(FOO)))" and return a
1459 custom_deallocator_set for them, consolidating them
1460 to ensure uniqueness of the sets.
1462 Return NULL if it has no such attributes.
1464 Subroutine of get_or_create_custom_deallocator_set which
1465 memoizes the result. */
1467 custom_deallocator_set
*
1468 malloc_state_machine::
1469 maybe_create_custom_deallocator_set (tree allocator_fndecl
)
1471 tree attrs
= DECL_ATTRIBUTES (allocator_fndecl
);
1474 /* Look for instances of __attribute__((malloc(FOO))). */
1475 auto_vec
<const deallocator
*> deallocator_vec
;
1476 for (tree allocs
= attrs
;
1477 (allocs
= lookup_attribute ("malloc", allocs
));
1478 allocs
= TREE_CHAIN (allocs
))
1480 tree args
= TREE_VALUE (allocs
);
1483 if (TREE_VALUE (args
))
1485 const deallocator
*d
1486 = get_or_create_deallocator (TREE_VALUE (args
));
1487 deallocator_vec
.safe_push (d
);
1491 /* If there weren't any deallocators, bail. */
1492 if (deallocator_vec
.length () == 0)
1495 /* Consolidate, so that we reuse existing deallocator_set
1497 deallocator_vec
.qsort (deallocator::cmp_ptr_ptr
);
1498 custom_deallocator_set
**slot
1499 = m_custom_deallocator_set_map
.get (&deallocator_vec
);
1502 custom_deallocator_set
*set
1503 = new custom_deallocator_set (this, &deallocator_vec
, WORDING_DEALLOCATED
);
1504 m_custom_deallocator_set_map
.put (&set
->m_deallocator_vec
, set
);
1505 m_dynamic_sets
.safe_push (set
);
1509 /* Get the deallocator for DEALLOCATOR_FNDECL, creating it if necessary. */
1512 malloc_state_machine::get_or_create_deallocator (tree deallocator_fndecl
)
1514 deallocator
**slot
= m_deallocator_map
.get (deallocator_fndecl
);
1520 if (is_named_call_p (deallocator_fndecl
, "free")
1521 || is_std_named_call_p (deallocator_fndecl
, "free")
1522 || is_named_call_p (deallocator_fndecl
, "__builtin_free"))
1523 d
= &m_free
.m_deallocator
;
1526 custom_deallocator
*cd
1527 = new custom_deallocator (this, deallocator_fndecl
,
1528 WORDING_DEALLOCATED
);
1529 m_dynamic_deallocators
.safe_push (cd
);
1532 m_deallocator_map
.put (deallocator_fndecl
, d
);
1536 /* Try to identify the function declaration either by name or as a known malloc
1540 known_allocator_p (const_tree fndecl
, const gcall
*call
)
1542 /* Either it is a function we know by name and number of arguments... */
1543 if (is_named_call_p (fndecl
, "malloc", call
, 1)
1544 || is_named_call_p (fndecl
, "calloc", call
, 2)
1545 || is_std_named_call_p (fndecl
, "malloc", call
, 1)
1546 || is_std_named_call_p (fndecl
, "calloc", call
, 2)
1547 || is_named_call_p (fndecl
, "strdup", call
, 1)
1548 || is_named_call_p (fndecl
, "strndup", call
, 2))
1551 /* ... or it is a builtin allocator that allocates objects freed with
1553 if (fndecl_built_in_p (fndecl
, BUILT_IN_NORMAL
))
1554 switch (DECL_FUNCTION_CODE (fndecl
))
1556 case BUILT_IN_MALLOC
:
1557 case BUILT_IN_CALLOC
:
1558 case BUILT_IN_STRDUP
:
1559 case BUILT_IN_STRNDUP
:
1568 /* Implementation of state_machine::on_stmt vfunc for malloc_state_machine. */
1571 malloc_state_machine::on_stmt (sm_context
*sm_ctxt
,
1572 const supernode
*node
,
1573 const gimple
*stmt
) const
1575 if (const gcall
*call
= dyn_cast
<const gcall
*> (stmt
))
1576 if (tree callee_fndecl
= sm_ctxt
->get_fndecl_for_call (call
))
1578 if (known_allocator_p (callee_fndecl
, call
))
1580 on_allocator_call (sm_ctxt
, call
, &m_free
);
1584 if (is_named_call_p (callee_fndecl
, "operator new", call
, 1))
1585 on_allocator_call (sm_ctxt
, call
, &m_scalar_delete
);
1586 else if (is_named_call_p (callee_fndecl
, "operator new []", call
, 1))
1587 on_allocator_call (sm_ctxt
, call
, &m_vector_delete
);
1588 else if (is_named_call_p (callee_fndecl
, "operator delete", call
, 1)
1589 || is_named_call_p (callee_fndecl
, "operator delete", call
, 2))
1591 on_deallocator_call (sm_ctxt
, node
, call
,
1592 &m_scalar_delete
.m_deallocator
, 0);
1595 else if (is_named_call_p (callee_fndecl
, "operator delete []", call
, 1))
1597 on_deallocator_call (sm_ctxt
, node
, call
,
1598 &m_vector_delete
.m_deallocator
, 0);
1602 if (is_named_call_p (callee_fndecl
, "alloca", call
, 1)
1603 || is_named_call_p (callee_fndecl
, "__builtin_alloca", call
, 1))
1605 tree lhs
= gimple_call_lhs (call
);
1607 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
, m_non_heap
);
1611 if (is_named_call_p (callee_fndecl
, "free", call
, 1)
1612 || is_std_named_call_p (callee_fndecl
, "free", call
, 1)
1613 || is_named_call_p (callee_fndecl
, "__builtin_free", call
, 1))
1615 on_deallocator_call (sm_ctxt
, node
, call
,
1616 &m_free
.m_deallocator
, 0);
1620 if (is_named_call_p (callee_fndecl
, "realloc", call
, 2)
1621 || is_named_call_p (callee_fndecl
, "__builtin_realloc", call
, 2))
1623 on_realloc_call (sm_ctxt
, node
, call
);
1627 if (unaffected_by_call_p (callee_fndecl
))
1630 /* Cast away const-ness for cache-like operations. */
1631 malloc_state_machine
*mutable_this
1632 = const_cast <malloc_state_machine
*> (this);
1634 /* Handle "__attribute__((malloc(FOO)))". */
1635 if (const deallocator_set
*deallocators
1636 = mutable_this
->get_or_create_custom_deallocator_set
1639 tree attrs
= TYPE_ATTRIBUTES (TREE_TYPE (callee_fndecl
));
1640 bool returns_nonnull
1641 = lookup_attribute ("returns_nonnull", attrs
);
1642 on_allocator_call (sm_ctxt
, call
, deallocators
, returns_nonnull
);
1645 /* Handle "__attribute__((nonnull))". */
1647 tree fntype
= TREE_TYPE (callee_fndecl
);
1648 bitmap nonnull_args
= get_nonnull_args (fntype
);
1651 for (unsigned i
= 0; i
< gimple_call_num_args (stmt
); i
++)
1653 tree arg
= gimple_call_arg (stmt
, i
);
1654 if (TREE_CODE (TREE_TYPE (arg
)) != POINTER_TYPE
)
1656 /* If we have a nonnull-args, and either all pointers, or just
1657 the specified pointers. */
1658 if (bitmap_empty_p (nonnull_args
)
1659 || bitmap_bit_p (nonnull_args
, i
))
1661 state_t state
= sm_ctxt
->get_state (stmt
, arg
);
1662 /* Can't use a switch as the states are non-const. */
1663 if (unchecked_p (state
))
1665 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1666 sm_ctxt
->warn (node
, stmt
, arg
,
1667 new possible_null_arg (*this, diag_arg
,
1670 const allocation_state
*astate
1671 = as_a_allocation_state (state
);
1672 sm_ctxt
->set_next_state (stmt
, arg
,
1673 astate
->get_nonnull ());
1675 else if (state
== m_null
)
1677 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1678 sm_ctxt
->warn (node
, stmt
, arg
,
1679 new null_arg (*this, diag_arg
,
1681 sm_ctxt
->set_next_state (stmt
, arg
, m_stop
);
1685 BITMAP_FREE (nonnull_args
);
1689 /* Check for this after nonnull, so that if we have both
1690 then we transition to "freed", rather than "checked". */
1691 unsigned dealloc_argno
= fndecl_dealloc_argno (callee_fndecl
);
1692 if (dealloc_argno
!= UINT_MAX
)
1694 const deallocator
*d
1695 = mutable_this
->get_or_create_deallocator (callee_fndecl
);
1696 on_deallocator_call (sm_ctxt
, node
, call
, d
, dealloc_argno
);
1700 if (tree lhs
= sm_ctxt
->is_zero_assignment (stmt
))
1701 if (any_pointer_p (lhs
))
1702 on_zero_assignment (sm_ctxt
, stmt
,lhs
);
1704 /* If we have "LHS = &EXPR;" and EXPR is something other than a MEM_REF,
1705 transition LHS from start to non_heap.
1706 Doing it for ADDR_EXPR(MEM_REF()) is likely wrong, and can lead to
1707 unbounded chains of unmergeable sm-state on pointer arithmetic in loops
1708 when optimization is enabled. */
1709 if (const gassign
*assign_stmt
= dyn_cast
<const gassign
*> (stmt
))
1711 enum tree_code op
= gimple_assign_rhs_code (assign_stmt
);
1712 if (op
== ADDR_EXPR
)
1714 tree lhs
= gimple_assign_lhs (assign_stmt
);
1717 tree addr_expr
= gimple_assign_rhs1 (assign_stmt
);
1718 if (TREE_CODE (TREE_OPERAND (addr_expr
, 0)) != MEM_REF
)
1719 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
, m_non_heap
);
1724 /* Handle dereferences. */
1725 for (unsigned i
= 0; i
< gimple_num_ops (stmt
); i
++)
1727 tree op
= gimple_op (stmt
, i
);
1730 if (TREE_CODE (op
) == COMPONENT_REF
)
1731 op
= TREE_OPERAND (op
, 0);
1733 if (TREE_CODE (op
) == MEM_REF
)
1735 tree arg
= TREE_OPERAND (op
, 0);
1737 state_t state
= sm_ctxt
->get_state (stmt
, arg
);
1738 if (unchecked_p (state
))
1740 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1741 sm_ctxt
->warn (node
, stmt
, arg
,
1742 new possible_null_deref (*this, diag_arg
));
1743 const allocation_state
*astate
= as_a_allocation_state (state
);
1744 sm_ctxt
->set_next_state (stmt
, arg
, astate
->get_nonnull ());
1746 else if (state
== m_null
)
1748 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1749 sm_ctxt
->warn (node
, stmt
, arg
,
1750 new null_deref (*this, diag_arg
));
1751 sm_ctxt
->set_next_state (stmt
, arg
, m_stop
);
1753 else if (freed_p (state
))
1755 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1756 const allocation_state
*astate
= as_a_allocation_state (state
);
1757 sm_ctxt
->warn (node
, stmt
, arg
,
1758 new use_after_free (*this, diag_arg
,
1759 astate
->m_deallocator
));
1760 sm_ctxt
->set_next_state (stmt
, arg
, m_stop
);
1767 /* Handle a call to an allocator.
1768 RETURNS_NONNULL is true if CALL is to a fndecl known to have
1769 __attribute__((returns_nonnull)). */
1772 malloc_state_machine::on_allocator_call (sm_context
*sm_ctxt
,
1774 const deallocator_set
*deallocators
,
1775 bool returns_nonnull
) const
1777 tree lhs
= gimple_call_lhs (call
);
1780 if (sm_ctxt
->get_state (call
, lhs
) == m_start
)
1781 sm_ctxt
->set_next_state (call
, lhs
,
1783 ? deallocators
->m_nonnull
1784 : deallocators
->m_unchecked
));
1788 /* TODO: report leak. */
1793 malloc_state_machine::on_deallocator_call (sm_context
*sm_ctxt
,
1794 const supernode
*node
,
1796 const deallocator
*d
,
1797 unsigned argno
) const
1799 if (argno
>= gimple_call_num_args (call
))
1801 tree arg
= gimple_call_arg (call
, argno
);
1803 state_t state
= sm_ctxt
->get_state (call
, arg
);
1805 /* start/unchecked/nonnull -> freed. */
1806 if (state
== m_start
)
1807 sm_ctxt
->set_next_state (call
, arg
, d
->m_freed
);
1808 else if (unchecked_p (state
) || nonnull_p (state
))
1810 const allocation_state
*astate
= as_a_allocation_state (state
);
1811 gcc_assert (astate
->m_deallocators
);
1812 if (!astate
->m_deallocators
->contains_p (d
))
1814 /* Wrong allocator. */
1815 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1816 pending_diagnostic
*pd
1817 = new mismatching_deallocation (*this, diag_arg
,
1818 astate
->m_deallocators
,
1820 sm_ctxt
->warn (node
, call
, arg
, pd
);
1822 sm_ctxt
->set_next_state (call
, arg
, d
->m_freed
);
1825 /* Keep state "null" as-is, rather than transitioning to "freed";
1826 we don't want to complain about double-free of NULL. */
1827 else if (state
== d
->m_freed
)
1829 /* freed -> stop, with warning. */
1830 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1831 sm_ctxt
->warn (node
, call
, arg
,
1832 new double_free (*this, diag_arg
, d
->m_name
));
1833 sm_ctxt
->set_next_state (call
, arg
, m_stop
);
1835 else if (state
== m_non_heap
)
1837 /* non-heap -> stop, with warning. */
1838 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1839 sm_ctxt
->warn (node
, call
, arg
,
1840 new free_of_non_heap (*this, diag_arg
,
1842 sm_ctxt
->set_next_state (call
, arg
, m_stop
);
1846 /* Handle a call to "realloc".
1847 Check for free of non-heap or mismatching allocators,
1848 transitioning to the "stop" state for such cases.
1850 Otherwise, region_model::impl_call_realloc will later
1851 get called (which will handle other sm-state transitions
1852 when the state is bifurcated). */
1855 malloc_state_machine::on_realloc_call (sm_context
*sm_ctxt
,
1856 const supernode
*node
,
1857 const gcall
*call
) const
1859 const unsigned argno
= 0;
1860 const deallocator
*d
= &m_realloc
;
1862 tree arg
= gimple_call_arg (call
, argno
);
1864 state_t state
= sm_ctxt
->get_state (call
, arg
);
1866 if (unchecked_p (state
) || nonnull_p (state
))
1868 const allocation_state
*astate
= as_a_allocation_state (state
);
1869 gcc_assert (astate
->m_deallocators
);
1870 if (!astate
->m_deallocators
->contains_p (&m_free
.m_deallocator
))
1872 /* Wrong allocator. */
1873 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1874 pending_diagnostic
*pd
1875 = new mismatching_deallocation (*this, diag_arg
,
1876 astate
->m_deallocators
,
1878 sm_ctxt
->warn (node
, call
, arg
, pd
);
1879 sm_ctxt
->set_next_state (call
, arg
, m_stop
);
1880 if (path_context
*path_ctxt
= sm_ctxt
->get_path_context ())
1881 path_ctxt
->terminate_path ();
1884 else if (state
== m_free
.m_deallocator
.m_freed
)
1886 /* freed -> stop, with warning. */
1887 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1888 sm_ctxt
->warn (node
, call
, arg
,
1889 new double_free (*this, diag_arg
, "free"));
1890 sm_ctxt
->set_next_state (call
, arg
, m_stop
);
1891 if (path_context
*path_ctxt
= sm_ctxt
->get_path_context ())
1892 path_ctxt
->terminate_path ();
1894 else if (state
== m_non_heap
)
1896 /* non-heap -> stop, with warning. */
1897 tree diag_arg
= sm_ctxt
->get_diagnostic_tree (arg
);
1898 sm_ctxt
->warn (node
, call
, arg
,
1899 new free_of_non_heap (*this, diag_arg
,
1901 sm_ctxt
->set_next_state (call
, arg
, m_stop
);
1902 if (path_context
*path_ctxt
= sm_ctxt
->get_path_context ())
1903 path_ctxt
->terminate_path ();
1907 /* Implementation of state_machine::on_phi vfunc for malloc_state_machine. */
1910 malloc_state_machine::on_phi (sm_context
*sm_ctxt
,
1911 const supernode
*node ATTRIBUTE_UNUSED
,
1917 tree lhs
= gimple_phi_result (phi
);
1918 on_zero_assignment (sm_ctxt
, phi
, lhs
);
1922 /* Implementation of state_machine::on_condition vfunc for malloc_state_machine.
1923 Potentially transition state 'unchecked' to 'nonnull' or to 'null'. */
1926 malloc_state_machine::on_condition (sm_context
*sm_ctxt
,
1927 const supernode
*node ATTRIBUTE_UNUSED
,
1931 const svalue
*rhs
) const
1933 if (!rhs
->all_zeroes_p ())
1936 if (!any_pointer_p (lhs
))
1938 if (!any_pointer_p (rhs
))
1943 log ("got 'ARG != 0' match");
1944 state_t s
= sm_ctxt
->get_state (stmt
, lhs
);
1945 if (unchecked_p (s
))
1947 const allocation_state
*astate
= as_a_allocation_state (s
);
1948 sm_ctxt
->set_next_state (stmt
, lhs
, astate
->get_nonnull ());
1951 else if (op
== EQ_EXPR
)
1953 log ("got 'ARG == 0' match");
1954 state_t s
= sm_ctxt
->get_state (stmt
, lhs
);
1955 if (unchecked_p (s
))
1956 sm_ctxt
->set_next_state (stmt
, lhs
, m_null
);
1960 /* Implementation of state_machine::can_purge_p vfunc for malloc_state_machine.
1961 Don't allow purging of pointers in state 'unchecked' or 'nonnull'
1962 (to avoid false leak reports). */
1965 malloc_state_machine::can_purge_p (state_t s
) const
1967 enum resource_state rs
= get_rs (s
);
1968 return rs
!= RS_UNCHECKED
&& rs
!= RS_NONNULL
;
1971 /* Implementation of state_machine::on_leak vfunc for malloc_state_machine
1972 (for complaining about leaks of pointers in state 'unchecked' and
1975 pending_diagnostic
*
1976 malloc_state_machine::on_leak (tree var
) const
1978 return new malloc_leak (*this, var
);
1981 /* Implementation of state_machine::reset_when_passed_to_unknown_fn_p vfunc
1982 for malloc_state_machine. */
1985 malloc_state_machine::reset_when_passed_to_unknown_fn_p (state_t s
,
1986 bool is_mutable
) const
1988 /* An on-stack ptr doesn't stop being stack-allocated when passed to an
1990 if (s
== m_non_heap
)
1993 /* Otherwise, pointers passed as non-const can be freed. */
1997 /* Return true if calls to FNDECL are known to not affect this sm-state. */
2000 malloc_state_machine::unaffected_by_call_p (tree fndecl
)
2002 /* A set of functions that are known to not affect allocation
2003 status, even if we haven't fully modelled the rest of their
2005 static const char * const funcnames
[] = {
2006 /* This array must be kept sorted. */
2010 = sizeof(funcnames
) / sizeof (funcnames
[0]);
2011 function_set
fs (funcnames
, count
);
2013 if (fs
.contains_decl_p (fndecl
))
2019 /* Shared logic for handling GIMPLE_ASSIGNs and GIMPLE_PHIs that
2020 assign zero to LHS. */
2023 malloc_state_machine::on_zero_assignment (sm_context
*sm_ctxt
,
2027 state_t s
= sm_ctxt
->get_state (stmt
, lhs
);
2028 enum resource_state rs
= get_rs (s
);
2030 || rs
== RS_UNCHECKED
2033 sm_ctxt
->set_next_state (stmt
, lhs
, m_null
);
2036 /* Special-case hook for handling realloc, for the "success with move to
2037 a new buffer" case, marking OLD_PTR_SVAL as freed and NEW_PTR_SVAL as
2040 This is similar to on_deallocator_call and on_allocator_call,
2041 but the checks happen in on_realloc_call, and by splitting the states. */
2044 malloc_state_machine::
2045 on_realloc_with_move (region_model
*model
,
2047 const svalue
*old_ptr_sval
,
2048 const svalue
*new_ptr_sval
,
2049 const extrinsic_state
&ext_state
) const
2051 smap
->set_state (model
, old_ptr_sval
,
2052 m_free
.m_deallocator
.m_freed
,
2055 smap
->set_state (model
, new_ptr_sval
,
2060 } // anonymous namespace
2062 /* Internal interface to this file. */
2065 make_malloc_state_machine (logger
*logger
)
2067 return new malloc_state_machine (logger
);
2070 /* Specialcase hook for handling realloc, for use by
2071 region_model::impl_call_realloc::success_with_move::update_model. */
2074 region_model::on_realloc_with_move (const call_details
&cd
,
2075 const svalue
*old_ptr_sval
,
2076 const svalue
*new_ptr_sval
)
2078 region_model_context
*ctxt
= cd
.get_ctxt ();
2081 const extrinsic_state
*ext_state
= ctxt
->get_ext_state ();
2086 const state_machine
*sm
;
2088 if (!ctxt
->get_malloc_map (&smap
, &sm
, &sm_idx
))
2094 const malloc_state_machine
&malloc_sm
2095 = (const malloc_state_machine
&)*sm
;
2097 malloc_sm
.on_realloc_with_move (this,
2106 #endif /* #if ENABLE_ANALYZER */