hppa: Export main in pr104869.C on hpux
[official-gcc.git] / gcc / analyzer / sm-malloc.cc
blob5af654414b491359580f3c5335dc25c81e09089d
1 /* A state machine for detecting misuses of the malloc/free API.
2 Copyright (C) 2019-2023 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)
10 any later version.
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/>. */
21 #include "config.h"
22 #define INCLUDE_MEMORY
23 #include "system.h"
24 #include "coretypes.h"
25 #include "make-unique.h"
26 #include "tree.h"
27 #include "function.h"
28 #include "basic-block.h"
29 #include "gimple.h"
30 #include "options.h"
31 #include "bitmap.h"
32 #include "diagnostic-path.h"
33 #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"
39 #include "analyzer/call-string.h"
40 #include "analyzer/program-point.h"
41 #include "analyzer/store.h"
42 #include "analyzer/region-model.h"
43 #include "analyzer/call-details.h"
44 #include "stringpool.h"
45 #include "attribs.h"
46 #include "analyzer/function-set.h"
47 #include "analyzer/program-state.h"
48 #include "analyzer/checker-event.h"
49 #include "analyzer/exploded-graph.h"
51 #if ENABLE_ANALYZER
53 namespace ana {
55 namespace {
57 /* This state machine and its various support classes track allocations
58 and deallocations.
60 It has a few standard allocation/deallocation pairs (e.g. new/delete),
61 and also supports user-defined ones via
62 __attribute__ ((malloc(DEALLOCATOR))).
64 There can be more than one valid deallocator for a given allocator,
65 for example:
66 __attribute__ ((malloc (fclose)))
67 __attribute__ ((malloc (freopen, 3)))
68 FILE* fopen (const char*, const char*);
69 A deallocator_set represents a particular set of valid deallocators.
71 We track the expected deallocator_set for a value, but not the allocation
72 function - there could be more than one allocator per deallocator_set.
73 For example, there could be dozens of allocators for "free" beyond just
74 malloc e.g. calloc, xstrdup, etc. We don't want to explode the number
75 of states by tracking individual allocators in the exploded graph;
76 we merely want to track "this value expects to have 'free' called on it".
77 Perhaps we can reconstruct which allocator was used later, when emitting
78 the path, if it's necessary for precision of wording of diagnostics. */
80 class deallocator;
81 class deallocator_set;
82 class malloc_state_machine;
84 /* An enum for discriminating between different kinds of allocation_state. */
86 enum resource_state
88 /* States that are independent of allocator/deallocator. */
90 /* The start state. */
91 RS_START,
93 /* State for a pointer that's been unconditionally dereferenced. */
94 RS_ASSUMED_NON_NULL,
96 /* State for a pointer that's known to be NULL. */
97 RS_NULL,
99 /* State for a pointer that's known to not be on the heap (e.g. to a local
100 or global). */
101 RS_NON_HEAP,
103 /* Stop state, for pointers we don't want to track any more. */
104 RS_STOP,
106 /* States that relate to a specific deallocator_set. */
108 /* State for a pointer returned from an allocator that hasn't
109 been checked for NULL.
110 It could be a pointer to heap-allocated memory, or could be NULL. */
111 RS_UNCHECKED,
113 /* State for a pointer returned from an allocator,
114 known to be non-NULL. */
115 RS_NONNULL,
117 /* State for a pointer passed to a deallocator. */
118 RS_FREED
121 /* Custom state subclass, which can optionally refer to an a
122 deallocator_set. */
124 struct allocation_state : public state_machine::state
126 allocation_state (const char *name, unsigned id,
127 enum resource_state rs,
128 const deallocator_set *deallocators,
129 const deallocator *deallocator)
130 : state (name, id), m_rs (rs),
131 m_deallocators (deallocators),
132 m_deallocator (deallocator)
135 void dump_to_pp (pretty_printer *pp) const override;
137 const allocation_state *get_nonnull () const;
139 enum resource_state m_rs;
140 const deallocator_set *m_deallocators;
141 const deallocator *m_deallocator;
144 /* Custom state subclass, for the "assumed-non-null" state
145 where the assumption happens in a particular frame. */
147 struct assumed_non_null_state : public allocation_state
149 assumed_non_null_state (const char *name, unsigned id,
150 const frame_region *frame)
151 : allocation_state (name, id, RS_ASSUMED_NON_NULL,
152 NULL, NULL),
153 m_frame (frame)
155 gcc_assert (m_frame);
158 void dump_to_pp (pretty_printer *pp) const final override;
160 const frame_region *m_frame;
163 /* An enum for choosing which wording to use in various diagnostics
164 when describing deallocations. */
166 enum wording
168 WORDING_FREED,
169 WORDING_DELETED,
170 WORDING_DEALLOCATED,
171 WORDING_REALLOCATED
174 /* Base class representing a deallocation function,
175 either a built-in one we know about, or one exposed via
176 __attribute__((malloc(DEALLOCATOR))). */
178 struct deallocator
180 hashval_t hash () const;
181 void dump_to_pp (pretty_printer *pp) const;
182 static int cmp (const deallocator *a, const deallocator *b);
183 static int cmp_ptr_ptr (const void *, const void *);
185 /* Name to use in diagnostics. */
186 const char *m_name;
188 /* Which wording to use in diagnostics. */
189 enum wording m_wording;
191 /* State for a value passed to one of the deallocators. */
192 state_machine::state_t m_freed;
194 protected:
195 deallocator (malloc_state_machine *sm,
196 const char *name,
197 enum wording wording);
200 /* Subclass representing a predefined deallocator.
201 e.g. "delete []", without needing a specific FUNCTION_DECL
202 ahead of time. */
204 struct standard_deallocator : public deallocator
206 standard_deallocator (malloc_state_machine *sm,
207 const char *name,
208 enum wording wording);
211 /* Subclass representing a user-defined deallocator
212 via __attribute__((malloc(DEALLOCATOR))) given
213 a specific FUNCTION_DECL. */
215 struct custom_deallocator : public deallocator
217 custom_deallocator (malloc_state_machine *sm,
218 tree deallocator_fndecl,
219 enum wording wording)
220 : deallocator (sm, IDENTIFIER_POINTER (DECL_NAME (deallocator_fndecl)),
221 wording)
226 /* Base class representing a set of possible deallocators.
227 Often this will be just a single deallocator, but some
228 allocators have multiple valid deallocators (e.g. the result of
229 "fopen" can be closed by either "fclose" or "freopen"). */
231 struct deallocator_set
233 deallocator_set (malloc_state_machine *sm,
234 enum wording wording);
235 virtual ~deallocator_set () {}
237 virtual bool contains_p (const deallocator *d) const = 0;
238 virtual const deallocator *maybe_get_single () const = 0;
239 virtual void dump_to_pp (pretty_printer *pp) const = 0;
240 void dump () const;
242 /* Which wording to use in diagnostics. */
243 enum wording m_wording;
245 /* Pointers to states.
246 These states are owned by the state_machine base class. */
248 /* State for an unchecked result from an allocator using this set. */
249 state_machine::state_t m_unchecked;
251 /* State for a known non-NULL result from such an allocator. */
252 state_machine::state_t m_nonnull;
255 /* Subclass of deallocator_set representing a set of deallocators
256 defined by one or more __attribute__((malloc(DEALLOCATOR))). */
258 struct custom_deallocator_set : public deallocator_set
260 typedef const auto_vec <const deallocator *> *key_t;
262 custom_deallocator_set (malloc_state_machine *sm,
263 const auto_vec <const deallocator *> *vec,
264 //const char *name,
265 //const char *dealloc_funcname,
266 //unsigned arg_idx,
267 enum wording wording);
269 bool contains_p (const deallocator *d) const final override;
270 const deallocator *maybe_get_single () const final override;
271 void dump_to_pp (pretty_printer *pp) const final override;
273 auto_vec <const deallocator *> m_deallocator_vec;
276 /* Subclass of deallocator_set representing a set of deallocators
277 with a single standard_deallocator, e.g. "delete []". */
279 struct standard_deallocator_set : public deallocator_set
281 standard_deallocator_set (malloc_state_machine *sm,
282 const char *name,
283 enum wording wording);
285 bool contains_p (const deallocator *d) const final override;
286 const deallocator *maybe_get_single () const final override;
287 void dump_to_pp (pretty_printer *pp) const final override;
289 standard_deallocator m_deallocator;
292 /* Traits class for ensuring uniqueness of deallocator_sets within
293 malloc_state_machine. */
295 struct deallocator_set_map_traits
297 typedef custom_deallocator_set::key_t key_type;
298 typedef custom_deallocator_set *value_type;
299 typedef custom_deallocator_set *compare_type;
301 static inline hashval_t hash (const key_type &k)
303 gcc_assert (k != NULL);
304 gcc_assert (k != reinterpret_cast<key_type> (1));
306 hashval_t result = 0;
307 unsigned i;
308 const deallocator *d;
309 FOR_EACH_VEC_ELT (*k, i, d)
310 result ^= d->hash ();
311 return result;
313 static inline bool equal_keys (const key_type &k1, const key_type &k2)
315 if (k1->length () != k2->length ())
316 return false;
318 for (unsigned i = 0; i < k1->length (); i++)
319 if ((*k1)[i] != (*k2)[i])
320 return false;
322 return true;
324 template <typename T>
325 static inline void remove (T &)
327 /* empty; the nodes are handled elsewhere. */
329 template <typename T>
330 static inline void mark_deleted (T &entry)
332 entry.m_key = reinterpret_cast<key_type> (1);
334 template <typename T>
335 static inline void mark_empty (T &entry)
337 entry.m_key = NULL;
339 template <typename T>
340 static inline bool is_deleted (const T &entry)
342 return entry.m_key == reinterpret_cast<key_type> (1);
344 template <typename T>
345 static inline bool is_empty (const T &entry)
347 return entry.m_key == NULL;
349 static const bool empty_zero_p = false;
352 /* A state machine for detecting misuses of the malloc/free API.
354 See sm-malloc.dot for an overview (keep this in-sync with that file). */
356 class malloc_state_machine : public state_machine
358 public:
359 typedef allocation_state custom_data_t;
361 malloc_state_machine (logger *logger);
362 ~malloc_state_machine ();
364 state_t
365 add_state (const char *name, enum resource_state rs,
366 const deallocator_set *deallocators,
367 const deallocator *deallocator);
369 bool inherited_state_p () const final override { return false; }
371 state_machine::state_t
372 get_default_state (const svalue *sval) const final override
374 if (tree cst = sval->maybe_get_constant ())
376 if (zerop (cst))
377 return m_null;
379 if (const region_svalue *ptr = sval->dyn_cast_region_svalue ())
381 const region *reg = ptr->get_pointee ();
382 switch (reg->get_memory_space ())
384 default:
385 break;
386 case MEMSPACE_CODE:
387 case MEMSPACE_GLOBALS:
388 case MEMSPACE_STACK:
389 case MEMSPACE_READONLY_DATA:
390 return m_non_heap;
393 return m_start;
396 bool on_stmt (sm_context *sm_ctxt,
397 const supernode *node,
398 const gimple *stmt) const final override;
400 void on_phi (sm_context *sm_ctxt,
401 const supernode *node,
402 const gphi *phi,
403 tree rhs) const final override;
405 void on_condition (sm_context *sm_ctxt,
406 const supernode *node,
407 const gimple *stmt,
408 const svalue *lhs,
409 enum tree_code op,
410 const svalue *rhs) const final override;
412 void on_pop_frame (sm_state_map *smap,
413 const frame_region *) const final override;
415 bool can_purge_p (state_t s) const final override;
416 std::unique_ptr<pending_diagnostic> on_leak (tree var) const final override;
418 bool reset_when_passed_to_unknown_fn_p (state_t s,
419 bool is_mutable) const final override;
421 state_t
422 maybe_get_merged_states_nonequal (state_t state_a,
423 state_t state_b) const final override;
425 static bool unaffected_by_call_p (tree fndecl);
427 void maybe_assume_non_null (sm_context *sm_ctxt,
428 tree ptr,
429 const gimple *stmt) const;
431 void on_realloc_with_move (region_model *model,
432 sm_state_map *smap,
433 const svalue *old_ptr_sval,
434 const svalue *new_ptr_sval,
435 const extrinsic_state &ext_state) const;
437 void transition_ptr_sval_non_null (region_model *model,
438 sm_state_map *smap,
439 const svalue *new_ptr_sval,
440 const extrinsic_state &ext_state) const;
442 standard_deallocator_set m_free;
443 standard_deallocator_set m_scalar_delete;
444 standard_deallocator_set m_vector_delete;
446 standard_deallocator m_realloc;
448 /* States that are independent of api. */
450 /* States for a pointer that's been unconditionally dereferenced
451 in a particular stack frame. */
452 hash_map<const frame_region *, state_t> m_assumed_non_null;
454 /* State for a pointer that's known to be NULL. */
455 state_t m_null;
457 /* State for a pointer that's known to not be on the heap (e.g. to a local
458 or global). */
459 state_t m_non_heap; // TODO: or should this be a different state machine?
460 // or do we need child values etc?
462 /* Stop state, for pointers we don't want to track any more. */
463 state_t m_stop;
465 private:
466 const custom_deallocator_set *
467 get_or_create_custom_deallocator_set (tree allocator_fndecl);
468 custom_deallocator_set *
469 maybe_create_custom_deallocator_set (tree allocator_fndecl);
470 const deallocator *
471 get_or_create_deallocator (tree deallocator_fndecl);
473 state_t
474 get_or_create_assumed_non_null_state_for_frame (const frame_region *frame);
476 void
477 maybe_complain_about_deref_before_check (sm_context *sm_ctxt,
478 const supernode *node,
479 const gimple *stmt,
480 const assumed_non_null_state *,
481 tree ptr) const;
483 void on_allocator_call (sm_context *sm_ctxt,
484 const gcall *call,
485 const deallocator_set *deallocators,
486 bool returns_nonnull = false) const;
487 void handle_free_of_non_heap (sm_context *sm_ctxt,
488 const supernode *node,
489 const gcall *call,
490 tree arg,
491 const deallocator *d) const;
492 void on_deallocator_call (sm_context *sm_ctxt,
493 const supernode *node,
494 const gcall *call,
495 const deallocator *d,
496 unsigned argno) const;
497 void on_realloc_call (sm_context *sm_ctxt,
498 const supernode *node,
499 const gcall *call) const;
500 void on_zero_assignment (sm_context *sm_ctxt,
501 const gimple *stmt,
502 tree lhs) const;
504 /* A map for consolidating deallocators so that they are
505 unique per deallocator FUNCTION_DECL. */
506 typedef hash_map<tree, deallocator *> deallocator_map_t;
507 deallocator_map_t m_deallocator_map;
509 /* Memoized lookups from FUNCTION_DECL to custom_deallocator_set *. */
510 typedef hash_map<tree, custom_deallocator_set *> deallocator_set_cache_t;
511 deallocator_set_cache_t m_custom_deallocator_set_cache;
513 /* A map for consolidating custom_deallocator_set instances. */
514 typedef hash_map<custom_deallocator_set::key_t,
515 custom_deallocator_set *,
516 deallocator_set_map_traits> custom_deallocator_set_map_t;
517 custom_deallocator_set_map_t m_custom_deallocator_set_map;
519 /* Record of dynamically-allocated objects, for cleanup. */
520 auto_vec <custom_deallocator_set *> m_dynamic_sets;
521 auto_vec <custom_deallocator *> m_dynamic_deallocators;
524 /* struct deallocator. */
526 deallocator::deallocator (malloc_state_machine *sm,
527 const char *name,
528 enum wording wording)
529 : m_name (name),
530 m_wording (wording),
531 m_freed (sm->add_state ("freed", RS_FREED, NULL, this))
535 hashval_t
536 deallocator::hash () const
538 return (hashval_t)m_freed->get_id ();
541 void
542 deallocator::dump_to_pp (pretty_printer *pp) const
544 pp_printf (pp, "%qs", m_name);
548 deallocator::cmp (const deallocator *a, const deallocator *b)
550 return (int)a->m_freed->get_id () - (int)b->m_freed->get_id ();
554 deallocator::cmp_ptr_ptr (const void *a, const void *b)
556 return cmp (*(const deallocator * const *)a,
557 *(const deallocator * const *)b);
561 /* struct standard_deallocator : public deallocator. */
563 standard_deallocator::standard_deallocator (malloc_state_machine *sm,
564 const char *name,
565 enum wording wording)
566 : deallocator (sm, name, wording)
570 /* struct deallocator_set. */
572 deallocator_set::deallocator_set (malloc_state_machine *sm,
573 enum wording wording)
574 : m_wording (wording),
575 m_unchecked (sm->add_state ("unchecked", RS_UNCHECKED, this, NULL)),
576 m_nonnull (sm->add_state ("nonnull", RS_NONNULL, this, NULL))
580 /* Dump a description of this deallocator_set to stderr. */
582 DEBUG_FUNCTION void
583 deallocator_set::dump () const
585 pretty_printer pp;
586 pp_show_color (&pp) = pp_show_color (global_dc->printer);
587 pp.buffer->stream = stderr;
588 dump_to_pp (&pp);
589 pp_newline (&pp);
590 pp_flush (&pp);
593 /* struct custom_deallocator_set : public deallocator_set. */
595 custom_deallocator_set::
596 custom_deallocator_set (malloc_state_machine *sm,
597 const auto_vec <const deallocator *> *vec,
598 enum wording wording)
599 : deallocator_set (sm, wording),
600 m_deallocator_vec (vec->length ())
602 unsigned i;
603 const deallocator *d;
604 FOR_EACH_VEC_ELT (*vec, i, d)
605 m_deallocator_vec.safe_push (d);
608 bool
609 custom_deallocator_set::contains_p (const deallocator *d) const
611 unsigned i;
612 const deallocator *cd;
613 FOR_EACH_VEC_ELT (m_deallocator_vec, i, cd)
614 if (cd == d)
615 return true;
616 return false;
619 const deallocator *
620 custom_deallocator_set::maybe_get_single () const
622 if (m_deallocator_vec.length () == 1)
623 return m_deallocator_vec[0];
624 return NULL;
627 void
628 custom_deallocator_set::dump_to_pp (pretty_printer *pp) const
630 pp_character (pp, '{');
631 unsigned i;
632 const deallocator *d;
633 FOR_EACH_VEC_ELT (m_deallocator_vec, i, d)
635 if (i > 0)
636 pp_string (pp, ", ");
637 d->dump_to_pp (pp);
639 pp_character (pp, '}');
642 /* struct standard_deallocator_set : public deallocator_set. */
644 standard_deallocator_set::standard_deallocator_set (malloc_state_machine *sm,
645 const char *name,
646 enum wording wording)
647 : deallocator_set (sm, wording),
648 m_deallocator (sm, name, wording)
652 bool
653 standard_deallocator_set::contains_p (const deallocator *d) const
655 return d == &m_deallocator;
658 const deallocator *
659 standard_deallocator_set::maybe_get_single () const
661 return &m_deallocator;
664 void
665 standard_deallocator_set::dump_to_pp (pretty_printer *pp) const
667 pp_character (pp, '{');
668 pp_string (pp, m_deallocator.m_name);
669 pp_character (pp, '}');
672 /* Return STATE cast to the custom state subclass, or NULL for the start state.
673 Everything should be an allocation_state apart from the start state. */
675 static const allocation_state *
676 dyn_cast_allocation_state (state_machine::state_t state)
678 if (state->get_id () == 0)
679 return NULL;
680 return static_cast <const allocation_state *> (state);
683 /* Return STATE cast to the custom state subclass, for a state that is
684 already known to not be the start state . */
686 static const allocation_state *
687 as_a_allocation_state (state_machine::state_t state)
689 gcc_assert (state->get_id () != 0);
690 return static_cast <const allocation_state *> (state);
693 /* Get the resource_state for STATE. */
695 static enum resource_state
696 get_rs (state_machine::state_t state)
698 if (const allocation_state *astate = dyn_cast_allocation_state (state))
699 return astate->m_rs;
700 else
701 return RS_START;
704 /* Return true if STATE is the start state. */
706 static bool
707 start_p (state_machine::state_t state)
709 return get_rs (state) == RS_START;
712 /* Return true if STATE is an unchecked result from an allocator. */
714 static bool
715 unchecked_p (state_machine::state_t state)
717 return get_rs (state) == RS_UNCHECKED;
720 /* Return true if STATE is a non-null result from an allocator. */
722 static bool
723 nonnull_p (state_machine::state_t state)
725 return get_rs (state) == RS_NONNULL;
728 /* Return true if STATE is a value that has been passed to a deallocator. */
730 static bool
731 freed_p (state_machine::state_t state)
733 return get_rs (state) == RS_FREED;
736 /* Return true if STATE is a value that has been assumed to be non-NULL. */
738 static bool
739 assumed_non_null_p (state_machine::state_t state)
741 return get_rs (state) == RS_ASSUMED_NON_NULL;
744 /* Class for diagnostics relating to malloc_state_machine. */
746 class malloc_diagnostic : public pending_diagnostic
748 public:
749 malloc_diagnostic (const malloc_state_machine &sm, tree arg)
750 : m_sm (sm), m_arg (arg)
753 bool subclass_equal_p (const pending_diagnostic &base_other) const override
755 return same_tree_p (m_arg, ((const malloc_diagnostic &)base_other).m_arg);
758 label_text describe_state_change (const evdesc::state_change &change)
759 override
761 if (change.m_old_state == m_sm.get_start_state ()
762 && (unchecked_p (change.m_new_state) || nonnull_p (change.m_new_state)))
763 // TODO: verify that it's the allocation stmt, not a copy
764 return label_text::borrow ("allocated here");
765 if (unchecked_p (change.m_old_state)
766 && nonnull_p (change.m_new_state))
768 if (change.m_expr)
769 return change.formatted_print ("assuming %qE is non-NULL",
770 change.m_expr);
771 else
772 return change.formatted_print ("assuming %qs is non-NULL",
773 "<unknown>");
775 if (change.m_new_state == m_sm.m_null)
777 if (unchecked_p (change.m_old_state))
779 if (change.m_expr)
780 return change.formatted_print ("assuming %qE is NULL",
781 change.m_expr);
782 else
783 return change.formatted_print ("assuming %qs is NULL",
784 "<unknown>");
786 else
788 if (change.m_expr)
789 return change.formatted_print ("%qE is NULL",
790 change.m_expr);
791 else
792 return change.formatted_print ("%qs is NULL",
793 "<unknown>");
797 return label_text ();
800 diagnostic_event::meaning
801 get_meaning_for_state_change (const evdesc::state_change &change)
802 const final override
804 if (change.m_old_state == m_sm.get_start_state ()
805 && unchecked_p (change.m_new_state))
806 return diagnostic_event::meaning (diagnostic_event::VERB_acquire,
807 diagnostic_event::NOUN_memory);
808 if (freed_p (change.m_new_state))
809 return diagnostic_event::meaning (diagnostic_event::VERB_release,
810 diagnostic_event::NOUN_memory);
811 return diagnostic_event::meaning ();
814 protected:
815 const malloc_state_machine &m_sm;
816 tree m_arg;
819 /* Concrete subclass for reporting mismatching allocator/deallocator
820 diagnostics. */
822 class mismatching_deallocation : public malloc_diagnostic
824 public:
825 mismatching_deallocation (const malloc_state_machine &sm, tree arg,
826 const deallocator_set *expected_deallocators,
827 const deallocator *actual_dealloc)
828 : malloc_diagnostic (sm, arg),
829 m_expected_deallocators (expected_deallocators),
830 m_actual_dealloc (actual_dealloc)
833 const char *get_kind () const final override
835 return "mismatching_deallocation";
838 int get_controlling_option () const final override
840 return OPT_Wanalyzer_mismatching_deallocation;
843 bool emit (rich_location *rich_loc, logger *) final override
845 auto_diagnostic_group d;
846 diagnostic_metadata m;
847 m.add_cwe (762); /* CWE-762: Mismatched Memory Management Routines. */
848 if (const deallocator *expected_dealloc
849 = m_expected_deallocators->maybe_get_single ())
850 return warning_meta (rich_loc, m, get_controlling_option (),
851 "%qE should have been deallocated with %qs"
852 " but was deallocated with %qs",
853 m_arg, expected_dealloc->m_name,
854 m_actual_dealloc->m_name);
855 else
856 return warning_meta (rich_loc, m, get_controlling_option (),
857 "%qs called on %qE returned from a mismatched"
858 " allocation function",
859 m_actual_dealloc->m_name, m_arg);
862 label_text describe_state_change (const evdesc::state_change &change)
863 final override
865 if (unchecked_p (change.m_new_state))
867 m_alloc_event = change.m_event_id;
868 if (const deallocator *expected_dealloc
869 = m_expected_deallocators->maybe_get_single ())
870 return change.formatted_print ("allocated here"
871 " (expects deallocation with %qs)",
872 expected_dealloc->m_name);
873 else
874 return change.formatted_print ("allocated here");
876 return malloc_diagnostic::describe_state_change (change);
879 label_text describe_final_event (const evdesc::final_event &ev) final override
881 if (m_alloc_event.known_p ())
883 if (const deallocator *expected_dealloc
884 = m_expected_deallocators->maybe_get_single ())
885 return ev.formatted_print
886 ("deallocated with %qs here;"
887 " allocation at %@ expects deallocation with %qs",
888 m_actual_dealloc->m_name, &m_alloc_event,
889 expected_dealloc->m_name);
890 else
891 return ev.formatted_print
892 ("deallocated with %qs here;"
893 " allocated at %@",
894 m_actual_dealloc->m_name, &m_alloc_event);
896 return ev.formatted_print ("deallocated with %qs here",
897 m_actual_dealloc->m_name);
900 private:
901 diagnostic_event_id_t m_alloc_event;
902 const deallocator_set *m_expected_deallocators;
903 const deallocator *m_actual_dealloc;
906 /* Concrete subclass for reporting double-free diagnostics. */
908 class double_free : public malloc_diagnostic
910 public:
911 double_free (const malloc_state_machine &sm, tree arg, const char *funcname)
912 : malloc_diagnostic (sm, arg), m_funcname (funcname)
915 const char *get_kind () const final override { return "double_free"; }
917 int get_controlling_option () const final override
919 return OPT_Wanalyzer_double_free;
922 bool emit (rich_location *rich_loc, logger *) final override
924 auto_diagnostic_group d;
925 diagnostic_metadata m;
926 m.add_cwe (415); /* CWE-415: Double Free. */
927 return warning_meta (rich_loc, m, get_controlling_option (),
928 "double-%qs of %qE", m_funcname, m_arg);
931 label_text describe_state_change (const evdesc::state_change &change)
932 final override
934 if (freed_p (change.m_new_state))
936 m_first_free_event = change.m_event_id;
937 return change.formatted_print ("first %qs here", m_funcname);
939 return malloc_diagnostic::describe_state_change (change);
942 label_text describe_call_with_state (const evdesc::call_with_state &info)
943 final override
945 if (freed_p (info.m_state))
946 return info.formatted_print
947 ("passing freed pointer %qE in call to %qE from %qE",
948 info.m_expr, info.m_callee_fndecl, info.m_caller_fndecl);
949 return label_text ();
952 label_text describe_final_event (const evdesc::final_event &ev) final override
954 if (m_first_free_event.known_p ())
955 return ev.formatted_print ("second %qs here; first %qs was at %@",
956 m_funcname, m_funcname,
957 &m_first_free_event);
958 return ev.formatted_print ("second %qs here", m_funcname);
961 private:
962 diagnostic_event_id_t m_first_free_event;
963 const char *m_funcname;
966 /* Abstract subclass for describing possible bad uses of NULL.
967 Responsible for describing the call that could return NULL. */
969 class possible_null : public malloc_diagnostic
971 public:
972 possible_null (const malloc_state_machine &sm, tree arg)
973 : malloc_diagnostic (sm, arg)
976 label_text describe_state_change (const evdesc::state_change &change)
977 final override
979 if (change.m_old_state == m_sm.get_start_state ()
980 && unchecked_p (change.m_new_state))
982 m_origin_of_unchecked_event = change.m_event_id;
983 return label_text::borrow ("this call could return NULL");
985 return malloc_diagnostic::describe_state_change (change);
988 label_text describe_return_of_state (const evdesc::return_of_state &info)
989 final override
991 if (unchecked_p (info.m_state))
992 return info.formatted_print ("possible return of NULL to %qE from %qE",
993 info.m_caller_fndecl, info.m_callee_fndecl);
994 return label_text ();
997 protected:
998 diagnostic_event_id_t m_origin_of_unchecked_event;
1001 /* Concrete subclass for describing dereference of a possible NULL
1002 value. */
1004 class possible_null_deref : public possible_null
1006 public:
1007 possible_null_deref (const malloc_state_machine &sm, tree arg)
1008 : possible_null (sm, arg)
1011 const char *get_kind () const final override { return "possible_null_deref"; }
1013 int get_controlling_option () const final override
1015 return OPT_Wanalyzer_possible_null_dereference;
1018 bool emit (rich_location *rich_loc, logger *) final override
1020 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
1021 diagnostic_metadata m;
1022 m.add_cwe (690);
1023 return warning_meta (rich_loc, m, get_controlling_option (),
1024 "dereference of possibly-NULL %qE", m_arg);
1027 label_text describe_final_event (const evdesc::final_event &ev) final override
1029 if (m_origin_of_unchecked_event.known_p ())
1030 return ev.formatted_print ("%qE could be NULL: unchecked value from %@",
1031 ev.m_expr,
1032 &m_origin_of_unchecked_event);
1033 else
1034 return ev.formatted_print ("%qE could be NULL", ev.m_expr);
1039 /* Return true if FNDECL is a C++ method. */
1041 static bool
1042 method_p (tree fndecl)
1044 return TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE;
1047 /* Return a 1-based description of ARG_IDX (0-based) of FNDECL.
1048 Compare with %P in the C++ FE (implemented in cp/error.cc: parm_to_string
1049 as called from cp_printer). */
1051 static label_text
1052 describe_argument_index (tree fndecl, int arg_idx)
1054 if (method_p (fndecl))
1055 if (arg_idx == 0)
1056 return label_text::borrow ("'this'");
1057 pretty_printer pp;
1058 pp_printf (&pp, "%u", arg_idx + 1 - method_p (fndecl));
1059 return label_text::take (xstrdup (pp_formatted_text (&pp)));
1062 /* Subroutine for use by possible_null_arg::emit and null_arg::emit.
1063 Issue a note informing that the pertinent argument must be non-NULL. */
1065 static void
1066 inform_nonnull_attribute (tree fndecl, int arg_idx)
1068 label_text arg_desc = describe_argument_index (fndecl, arg_idx);
1069 inform (DECL_SOURCE_LOCATION (fndecl),
1070 "argument %s of %qD must be non-null",
1071 arg_desc.get (), fndecl);
1072 /* Ideally we would use the location of the parm and underline the
1073 attribute also - but we don't have the location_t values at this point
1074 in the middle-end.
1075 For reference, the C and C++ FEs have get_fndecl_argument_location. */
1078 /* Concrete subclass for describing passing a possibly-NULL value to a
1079 function marked with __attribute__((nonnull)). */
1081 class possible_null_arg : public possible_null
1083 public:
1084 possible_null_arg (const malloc_state_machine &sm, tree arg,
1085 tree fndecl, int arg_idx)
1086 : possible_null (sm, arg),
1087 m_fndecl (fndecl), m_arg_idx (arg_idx)
1090 const char *get_kind () const final override { return "possible_null_arg"; }
1092 bool subclass_equal_p (const pending_diagnostic &base_other)
1093 const final override
1095 const possible_null_arg &sub_other
1096 = (const possible_null_arg &)base_other;
1097 return (same_tree_p (m_arg, sub_other.m_arg)
1098 && m_fndecl == sub_other.m_fndecl
1099 && m_arg_idx == sub_other.m_arg_idx);
1102 int get_controlling_option () const final override
1104 return OPT_Wanalyzer_possible_null_argument;
1107 bool emit (rich_location *rich_loc, logger *) final override
1109 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
1110 auto_diagnostic_group d;
1111 diagnostic_metadata m;
1112 m.add_cwe (690);
1113 bool warned
1114 = warning_meta (rich_loc, m, get_controlling_option (),
1115 "use of possibly-NULL %qE where non-null expected",
1116 m_arg);
1117 if (warned)
1118 inform_nonnull_attribute (m_fndecl, m_arg_idx);
1119 return warned;
1122 label_text describe_final_event (const evdesc::final_event &ev) final override
1124 label_text arg_desc = describe_argument_index (m_fndecl, m_arg_idx);
1125 label_text result;
1126 if (m_origin_of_unchecked_event.known_p ())
1127 result = ev.formatted_print ("argument %s (%qE) from %@ could be NULL"
1128 " where non-null expected",
1129 arg_desc.get (), ev.m_expr,
1130 &m_origin_of_unchecked_event);
1131 else
1132 result = ev.formatted_print ("argument %s (%qE) could be NULL"
1133 " where non-null expected",
1134 arg_desc.get (), ev.m_expr);
1135 return result;
1138 private:
1139 tree m_fndecl;
1140 int m_arg_idx;
1143 /* Concrete subclass for describing a dereference of a NULL value. */
1145 class null_deref : public malloc_diagnostic
1147 public:
1148 null_deref (const malloc_state_machine &sm, tree arg)
1149 : malloc_diagnostic (sm, arg) {}
1151 const char *get_kind () const final override { return "null_deref"; }
1153 int get_controlling_option () const final override
1155 return OPT_Wanalyzer_null_dereference;
1158 bool terminate_path_p () const final override { return true; }
1160 bool emit (rich_location *rich_loc, logger *) final override
1162 /* CWE-476: NULL Pointer Dereference. */
1163 diagnostic_metadata m;
1164 m.add_cwe (476);
1165 return warning_meta (rich_loc, m, get_controlling_option (),
1166 "dereference of NULL %qE", m_arg);
1169 label_text describe_return_of_state (const evdesc::return_of_state &info)
1170 final override
1172 if (info.m_state == m_sm.m_null)
1173 return info.formatted_print ("return of NULL to %qE from %qE",
1174 info.m_caller_fndecl, info.m_callee_fndecl);
1175 return label_text ();
1178 label_text describe_final_event (const evdesc::final_event &ev) final override
1180 return ev.formatted_print ("dereference of NULL %qE", ev.m_expr);
1183 /* Implementation of pending_diagnostic::supercedes_p for
1184 null-deref.
1186 We want null-deref to supercede use-of-unitialized-value,
1187 so that if we have these at the same stmt, we don't emit
1188 a use-of-uninitialized, just the null-deref. */
1190 bool supercedes_p (const pending_diagnostic &other) const final override
1192 if (other.use_of_uninit_p ())
1193 return true;
1195 return false;
1199 /* Concrete subclass for describing passing a NULL value to a
1200 function marked with __attribute__((nonnull)). */
1202 class null_arg : public malloc_diagnostic
1204 public:
1205 null_arg (const malloc_state_machine &sm, tree arg,
1206 tree fndecl, int arg_idx)
1207 : malloc_diagnostic (sm, arg),
1208 m_fndecl (fndecl), m_arg_idx (arg_idx)
1211 const char *get_kind () const final override { return "null_arg"; }
1213 bool subclass_equal_p (const pending_diagnostic &base_other)
1214 const final override
1216 const null_arg &sub_other
1217 = (const null_arg &)base_other;
1218 return (same_tree_p (m_arg, sub_other.m_arg)
1219 && m_fndecl == sub_other.m_fndecl
1220 && m_arg_idx == sub_other.m_arg_idx);
1223 int get_controlling_option () const final override
1225 return OPT_Wanalyzer_null_argument;
1228 bool terminate_path_p () const final override { return true; }
1230 bool emit (rich_location *rich_loc, logger *) final override
1232 /* CWE-476: NULL Pointer Dereference. */
1233 auto_diagnostic_group d;
1234 diagnostic_metadata m;
1235 m.add_cwe (476);
1237 bool warned;
1238 if (zerop (m_arg))
1239 warned = warning_meta (rich_loc, m, get_controlling_option (),
1240 "use of NULL where non-null expected");
1241 else
1242 warned = warning_meta (rich_loc, m, get_controlling_option (),
1243 "use of NULL %qE where non-null expected",
1244 m_arg);
1245 if (warned)
1246 inform_nonnull_attribute (m_fndecl, m_arg_idx);
1247 return warned;
1250 label_text describe_final_event (const evdesc::final_event &ev) final override
1252 label_text arg_desc = describe_argument_index (m_fndecl, m_arg_idx);
1253 label_text result;
1254 if (zerop (ev.m_expr))
1255 result = ev.formatted_print ("argument %s NULL where non-null expected",
1256 arg_desc.get ());
1257 else
1258 result = ev.formatted_print ("argument %s (%qE) NULL"
1259 " where non-null expected",
1260 arg_desc.get (), ev.m_expr);
1261 return result;
1264 private:
1265 tree m_fndecl;
1266 int m_arg_idx;
1269 class use_after_free : public malloc_diagnostic
1271 public:
1272 use_after_free (const malloc_state_machine &sm, tree arg,
1273 const deallocator *deallocator)
1274 : malloc_diagnostic (sm, arg),
1275 m_deallocator (deallocator)
1277 gcc_assert (deallocator);
1280 const char *get_kind () const final override { return "use_after_free"; }
1282 int get_controlling_option () const final override
1284 return OPT_Wanalyzer_use_after_free;
1287 bool emit (rich_location *rich_loc, logger *) final override
1289 /* CWE-416: Use After Free. */
1290 diagnostic_metadata m;
1291 m.add_cwe (416);
1292 return warning_meta (rich_loc, m, get_controlling_option (),
1293 "use after %<%s%> of %qE",
1294 m_deallocator->m_name, m_arg);
1297 label_text describe_state_change (const evdesc::state_change &change)
1298 final override
1300 if (freed_p (change.m_new_state))
1302 m_free_event = change.m_event_id;
1303 switch (m_deallocator->m_wording)
1305 default:
1306 case WORDING_REALLOCATED:
1307 gcc_unreachable ();
1308 case WORDING_FREED:
1309 return label_text::borrow ("freed here");
1310 case WORDING_DELETED:
1311 return label_text::borrow ("deleted here");
1312 case WORDING_DEALLOCATED:
1313 return label_text::borrow ("deallocated here");
1316 return malloc_diagnostic::describe_state_change (change);
1319 label_text describe_final_event (const evdesc::final_event &ev) final override
1321 const char *funcname = m_deallocator->m_name;
1322 if (m_free_event.known_p ())
1323 switch (m_deallocator->m_wording)
1325 default:
1326 case WORDING_REALLOCATED:
1327 gcc_unreachable ();
1328 case WORDING_FREED:
1329 return ev.formatted_print ("use after %<%s%> of %qE; freed at %@",
1330 funcname, ev.m_expr, &m_free_event);
1331 case WORDING_DELETED:
1332 return ev.formatted_print ("use after %<%s%> of %qE; deleted at %@",
1333 funcname, ev.m_expr, &m_free_event);
1334 case WORDING_DEALLOCATED:
1335 return ev.formatted_print ("use after %<%s%> of %qE;"
1336 " deallocated at %@",
1337 funcname, ev.m_expr, &m_free_event);
1339 else
1340 return ev.formatted_print ("use after %<%s%> of %qE",
1341 funcname, ev.m_expr);
1344 /* Implementation of pending_diagnostic::supercedes_p for
1345 use_after_free.
1347 We want use-after-free to supercede use-of-unitialized-value,
1348 so that if we have these at the same stmt, we don't emit
1349 a use-of-uninitialized, just the use-after-free.
1350 (this is because we fully purge information about freed
1351 buffers when we free them to avoid state explosions, so
1352 that if they are accessed after the free, it looks like
1353 they are uninitialized). */
1355 bool supercedes_p (const pending_diagnostic &other) const final override
1357 if (other.use_of_uninit_p ())
1358 return true;
1360 return false;
1363 private:
1364 diagnostic_event_id_t m_free_event;
1365 const deallocator *m_deallocator;
1368 class malloc_leak : public malloc_diagnostic
1370 public:
1371 malloc_leak (const malloc_state_machine &sm, tree arg)
1372 : malloc_diagnostic (sm, arg) {}
1374 const char *get_kind () const final override { return "malloc_leak"; }
1376 int get_controlling_option () const final override
1378 return OPT_Wanalyzer_malloc_leak;
1381 bool emit (rich_location *rich_loc, logger *) final override
1383 /* "CWE-401: Missing Release of Memory after Effective Lifetime". */
1384 diagnostic_metadata m;
1385 m.add_cwe (401);
1386 if (m_arg)
1387 return warning_meta (rich_loc, m, get_controlling_option (),
1388 "leak of %qE", m_arg);
1389 else
1390 return warning_meta (rich_loc, m, get_controlling_option (),
1391 "leak of %qs", "<unknown>");
1394 label_text describe_state_change (const evdesc::state_change &change)
1395 final override
1397 if (unchecked_p (change.m_new_state)
1398 || (start_p (change.m_old_state) && nonnull_p (change.m_new_state)))
1400 m_alloc_event = change.m_event_id;
1401 return label_text::borrow ("allocated here");
1403 return malloc_diagnostic::describe_state_change (change);
1406 label_text describe_final_event (const evdesc::final_event &ev) final override
1408 if (ev.m_expr)
1410 if (m_alloc_event.known_p ())
1411 return ev.formatted_print ("%qE leaks here; was allocated at %@",
1412 ev.m_expr, &m_alloc_event);
1413 else
1414 return ev.formatted_print ("%qE leaks here", ev.m_expr);
1416 else
1418 if (m_alloc_event.known_p ())
1419 return ev.formatted_print ("%qs leaks here; was allocated at %@",
1420 "<unknown>", &m_alloc_event);
1421 else
1422 return ev.formatted_print ("%qs leaks here", "<unknown>");
1426 private:
1427 diagnostic_event_id_t m_alloc_event;
1430 class free_of_non_heap : public malloc_diagnostic
1432 public:
1433 free_of_non_heap (const malloc_state_machine &sm, tree arg,
1434 const region *freed_reg,
1435 const char *funcname)
1436 : malloc_diagnostic (sm, arg), m_freed_reg (freed_reg), m_funcname (funcname)
1440 const char *get_kind () const final override { return "free_of_non_heap"; }
1442 bool subclass_equal_p (const pending_diagnostic &base_other) const
1443 final override
1445 const free_of_non_heap &other = (const free_of_non_heap &)base_other;
1446 return (same_tree_p (m_arg, other.m_arg)
1447 && m_freed_reg == other.m_freed_reg);
1450 int get_controlling_option () const final override
1452 return OPT_Wanalyzer_free_of_non_heap;
1455 bool emit (rich_location *rich_loc, logger *) final override
1457 auto_diagnostic_group d;
1458 diagnostic_metadata m;
1459 m.add_cwe (590); /* CWE-590: Free of Memory not on the Heap. */
1460 switch (get_memory_space ())
1462 default:
1463 case MEMSPACE_HEAP:
1464 gcc_unreachable ();
1465 case MEMSPACE_UNKNOWN:
1466 case MEMSPACE_CODE:
1467 case MEMSPACE_GLOBALS:
1468 case MEMSPACE_READONLY_DATA:
1469 return warning_meta (rich_loc, m, get_controlling_option (),
1470 "%<%s%> of %qE which points to memory"
1471 " not on the heap",
1472 m_funcname, m_arg);
1473 break;
1474 case MEMSPACE_STACK:
1475 return warning_meta (rich_loc, m, get_controlling_option (),
1476 "%<%s%> of %qE which points to memory"
1477 " on the stack",
1478 m_funcname, m_arg);
1479 break;
1483 label_text describe_state_change (const evdesc::state_change &)
1484 final override
1486 return label_text::borrow ("pointer is from here");
1489 label_text describe_final_event (const evdesc::final_event &ev) final override
1491 return ev.formatted_print ("call to %qs here", m_funcname);
1494 void mark_interesting_stuff (interesting_t *interest) final override
1496 if (m_freed_reg)
1497 interest->add_region_creation (m_freed_reg);
1500 private:
1501 enum memory_space get_memory_space () const
1503 if (m_freed_reg)
1504 return m_freed_reg->get_memory_space ();
1505 else
1506 return MEMSPACE_UNKNOWN;
1509 const region *m_freed_reg;
1510 const char *m_funcname;
1513 /* Concrete pending_diagnostic subclass for -Wanalyzer-deref-before-check. */
1515 class deref_before_check : public malloc_diagnostic
1517 public:
1518 deref_before_check (const malloc_state_machine &sm, tree arg)
1519 : malloc_diagnostic (sm, arg),
1520 m_deref_enode (NULL),
1521 m_deref_expr (NULL),
1522 m_check_enode (NULL)
1524 gcc_assert (arg);
1527 const char *get_kind () const final override { return "deref_before_check"; }
1529 int get_controlling_option () const final override
1531 return OPT_Wanalyzer_deref_before_check;
1534 bool emit (rich_location *rich_loc, logger *) final override
1536 /* Don't emit the warning if we can't show where the deref
1537 and the check occur. */
1538 if (!m_deref_enode)
1539 return false;
1540 if (!m_check_enode)
1541 return false;
1542 /* Only emit the warning for intraprocedural cases. */
1543 const program_point &deref_point = m_deref_enode->get_point ();
1544 const program_point &check_point = m_check_enode->get_point ();
1546 if (!program_point::effectively_intraprocedural_p (deref_point,
1547 check_point))
1548 return false;
1550 /* Reject the warning if the check occurs within a macro defintion.
1551 This avoids false positives for such code as:
1553 #define throw_error \
1554 do { \
1555 if (p) \
1556 cleanup (p); \
1557 return; \
1558 } while (0)
1560 if (p->idx >= n)
1561 throw_error ();
1563 where the usage of "throw_error" implicitly adds a check
1564 on 'p'.
1566 We do warn when the check is in a macro expansion if we can get
1567 at the location of the condition and it is't part of the
1568 definition, so that we warn for checks such as:
1569 if (words[0][0] == '@')
1570 return;
1571 g_assert(words[0] != NULL); <--- here
1572 Unfortunately we don't have locations for individual gimple
1573 arguments, so in:
1574 g_assert (ptr);
1575 we merely have a gimple_cond
1576 if (p_2(D) == 0B)
1577 with no way of getting at the location of the condition separately
1578 from that of the gimple_cond (where the "if" is within the macro
1579 definition). We reject the warning for such cases.
1581 We do warn when the *deref* occurs in a macro, since this can be
1582 a source of real bugs; see e.g. PR 77425. */
1583 location_t check_loc = m_check_enode->get_point ().get_location ();
1584 if (linemap_location_from_macro_definition_p (line_table, check_loc))
1585 return false;
1587 /* Reject if m_deref_expr is sufficiently different from m_arg
1588 for cases where the dereference is spelled differently from
1589 the check, which is probably two different ways to get the
1590 same svalue, and thus not worth reporting. */
1591 if (!m_deref_expr)
1592 return false;
1593 if (!sufficiently_similar_p (m_deref_expr, m_arg))
1594 return false;
1596 /* Reject the warning if the deref's BB doesn't dominate that
1597 of the check, so that we don't warn e.g. for shared cleanup
1598 code that checks a pointer for NULL, when that code is sometimes
1599 used before a deref and sometimes after.
1600 Using the dominance code requires setting cfun. */
1601 auto_cfun sentinel (m_deref_enode->get_function ());
1602 calculate_dominance_info (CDI_DOMINATORS);
1603 if (!dominated_by_p (CDI_DOMINATORS,
1604 m_check_enode->get_supernode ()->m_bb,
1605 m_deref_enode->get_supernode ()->m_bb))
1606 return false;
1608 return warning_at (rich_loc, get_controlling_option (),
1609 "check of %qE for NULL after already"
1610 " dereferencing it",
1611 m_arg);
1614 label_text describe_state_change (const evdesc::state_change &change)
1615 final override
1617 if (change.m_old_state == m_sm.get_start_state ()
1618 && assumed_non_null_p (change.m_new_state))
1620 m_first_deref_event = change.m_event_id;
1621 m_deref_enode = change.m_event.get_exploded_node ();
1622 m_deref_expr = change.m_expr;
1623 return change.formatted_print ("pointer %qE is dereferenced here",
1624 m_arg);
1626 return malloc_diagnostic::describe_state_change (change);
1629 label_text describe_final_event (const evdesc::final_event &ev) final override
1631 m_check_enode = ev.m_event.get_exploded_node ();
1632 if (m_first_deref_event.known_p ())
1633 return ev.formatted_print ("pointer %qE is checked for NULL here but"
1634 " it was already dereferenced at %@",
1635 m_arg, &m_first_deref_event);
1636 else
1637 return ev.formatted_print ("pointer %qE is checked for NULL here but"
1638 " it was already dereferenced",
1639 m_arg);
1642 private:
1643 static bool sufficiently_similar_p (tree expr_a, tree expr_b)
1645 pretty_printer *pp_a = global_dc->printer->clone ();
1646 pretty_printer *pp_b = global_dc->printer->clone ();
1647 pp_printf (pp_a, "%qE", expr_a);
1648 pp_printf (pp_b, "%qE", expr_b);
1649 bool result = (strcmp (pp_formatted_text (pp_a), pp_formatted_text (pp_b))
1650 == 0);
1651 delete pp_a;
1652 delete pp_b;
1653 return result;
1656 diagnostic_event_id_t m_first_deref_event;
1657 const exploded_node *m_deref_enode;
1658 tree m_deref_expr;
1659 const exploded_node *m_check_enode;
1662 /* struct allocation_state : public state_machine::state. */
1664 /* Implementation of state_machine::state::dump_to_pp vfunc
1665 for allocation_state: append the API that this allocation is
1666 associated with. */
1668 void
1669 allocation_state::dump_to_pp (pretty_printer *pp) const
1671 state_machine::state::dump_to_pp (pp);
1672 if (m_deallocators)
1674 pp_string (pp, " (");
1675 m_deallocators->dump_to_pp (pp);
1676 pp_character (pp, ')');
1680 /* Given a allocation_state for a deallocator_set, get the "nonnull" state
1681 for the corresponding allocator(s). */
1683 const allocation_state *
1684 allocation_state::get_nonnull () const
1686 gcc_assert (m_deallocators);
1687 return as_a_allocation_state (m_deallocators->m_nonnull);
1690 /* struct assumed_non_null_state : public allocation_state. */
1692 void
1693 assumed_non_null_state::dump_to_pp (pretty_printer *pp) const
1695 allocation_state::dump_to_pp (pp);
1696 pp_string (pp, " (in ");
1697 m_frame->dump_to_pp (pp, true);
1698 pp_character (pp, ')');
1701 /* malloc_state_machine's ctor. */
1703 malloc_state_machine::malloc_state_machine (logger *logger)
1704 : state_machine ("malloc", logger),
1705 m_free (this, "free", WORDING_FREED),
1706 m_scalar_delete (this, "delete", WORDING_DELETED),
1707 m_vector_delete (this, "delete[]", WORDING_DELETED),
1708 m_realloc (this, "realloc", WORDING_REALLOCATED)
1710 gcc_assert (m_start->get_id () == 0);
1711 m_null = add_state ("null", RS_FREED, NULL, NULL);
1712 m_non_heap = add_state ("non-heap", RS_NON_HEAP, NULL, NULL);
1713 m_stop = add_state ("stop", RS_STOP, NULL, NULL);
1716 malloc_state_machine::~malloc_state_machine ()
1718 unsigned i;
1719 custom_deallocator_set *set;
1720 FOR_EACH_VEC_ELT (m_dynamic_sets, i, set)
1721 delete set;
1722 custom_deallocator *d;
1723 FOR_EACH_VEC_ELT (m_dynamic_deallocators, i, d)
1724 delete d;
1727 state_machine::state_t
1728 malloc_state_machine::add_state (const char *name, enum resource_state rs,
1729 const deallocator_set *deallocators,
1730 const deallocator *deallocator)
1732 return add_custom_state (new allocation_state (name, alloc_state_id (),
1733 rs, deallocators,
1734 deallocator));
1737 /* If ALLOCATOR_FNDECL has any "__attribute__((malloc(FOO)))",
1738 return a custom_deallocator_set for them, consolidating them
1739 to ensure uniqueness of the sets.
1741 Return NULL if it has no such attributes. */
1743 const custom_deallocator_set *
1744 malloc_state_machine::
1745 get_or_create_custom_deallocator_set (tree allocator_fndecl)
1747 /* Early rejection of decls without attributes. */
1748 tree attrs = DECL_ATTRIBUTES (allocator_fndecl);
1749 if (!attrs)
1750 return NULL;
1752 /* Otherwise, call maybe_create_custom_deallocator_set,
1753 memoizing the result. */
1754 if (custom_deallocator_set **slot
1755 = m_custom_deallocator_set_cache.get (allocator_fndecl))
1756 return *slot;
1757 custom_deallocator_set *set
1758 = maybe_create_custom_deallocator_set (allocator_fndecl);
1759 m_custom_deallocator_set_cache.put (allocator_fndecl, set);
1760 return set;
1763 /* Given ALLOCATOR_FNDECL, a FUNCTION_DECL with attributes,
1764 look for any "__attribute__((malloc(FOO)))" and return a
1765 custom_deallocator_set for them, consolidating them
1766 to ensure uniqueness of the sets.
1768 Return NULL if it has no such attributes.
1770 Subroutine of get_or_create_custom_deallocator_set which
1771 memoizes the result. */
1773 custom_deallocator_set *
1774 malloc_state_machine::
1775 maybe_create_custom_deallocator_set (tree allocator_fndecl)
1777 tree attrs = DECL_ATTRIBUTES (allocator_fndecl);
1778 gcc_assert (attrs);
1780 /* Look for instances of __attribute__((malloc(FOO))). */
1781 auto_vec<const deallocator *> deallocator_vec;
1782 for (tree allocs = attrs;
1783 (allocs = lookup_attribute ("malloc", allocs));
1784 allocs = TREE_CHAIN (allocs))
1786 tree args = TREE_VALUE (allocs);
1787 if (!args)
1788 continue;
1789 if (TREE_VALUE (args))
1791 const deallocator *d
1792 = get_or_create_deallocator (TREE_VALUE (args));
1793 deallocator_vec.safe_push (d);
1797 /* If there weren't any deallocators, bail. */
1798 if (deallocator_vec.length () == 0)
1799 return NULL;
1801 /* Consolidate, so that we reuse existing deallocator_set
1802 instances. */
1803 deallocator_vec.qsort (deallocator::cmp_ptr_ptr);
1804 custom_deallocator_set **slot
1805 = m_custom_deallocator_set_map.get (&deallocator_vec);
1806 if (slot)
1807 return *slot;
1808 custom_deallocator_set *set
1809 = new custom_deallocator_set (this, &deallocator_vec, WORDING_DEALLOCATED);
1810 m_custom_deallocator_set_map.put (&set->m_deallocator_vec, set);
1811 m_dynamic_sets.safe_push (set);
1812 return set;
1815 /* Get the deallocator for DEALLOCATOR_FNDECL, creating it if necessary. */
1817 const deallocator *
1818 malloc_state_machine::get_or_create_deallocator (tree deallocator_fndecl)
1820 deallocator **slot = m_deallocator_map.get (deallocator_fndecl);
1821 if (slot)
1822 return *slot;
1824 /* Reuse "free". */
1825 deallocator *d;
1826 if (is_named_call_p (deallocator_fndecl, "free")
1827 || is_std_named_call_p (deallocator_fndecl, "free")
1828 || is_named_call_p (deallocator_fndecl, "__builtin_free"))
1829 d = &m_free.m_deallocator;
1830 else
1832 custom_deallocator *cd
1833 = new custom_deallocator (this, deallocator_fndecl,
1834 WORDING_DEALLOCATED);
1835 m_dynamic_deallocators.safe_push (cd);
1836 d = cd;
1838 m_deallocator_map.put (deallocator_fndecl, d);
1839 return d;
1842 /* Get the "assumed-non-null" state for assumptions made within FRAME,
1843 creating it if necessary. */
1845 state_machine::state_t
1846 malloc_state_machine::
1847 get_or_create_assumed_non_null_state_for_frame (const frame_region *frame)
1849 if (state_t *slot = m_assumed_non_null.get (frame))
1850 return *slot;
1851 state_machine::state *new_state
1852 = new assumed_non_null_state ("assumed-non-null", alloc_state_id (), frame);
1853 add_custom_state (new_state);
1854 m_assumed_non_null.put (frame, new_state);
1855 return new_state;
1858 /* Try to identify the function declaration either by name or as a known malloc
1859 builtin. */
1861 static bool
1862 known_allocator_p (const_tree fndecl, const gcall *call)
1864 /* Either it is a function we know by name and number of arguments... */
1865 if (is_named_call_p (fndecl, "malloc", call, 1)
1866 || is_named_call_p (fndecl, "calloc", call, 2)
1867 || is_std_named_call_p (fndecl, "malloc", call, 1)
1868 || is_std_named_call_p (fndecl, "calloc", call, 2)
1869 || is_named_call_p (fndecl, "strdup", call, 1)
1870 || is_named_call_p (fndecl, "strndup", call, 2))
1871 return true;
1873 /* ... or it is a builtin allocator that allocates objects freed with
1874 __builtin_free. */
1875 if (fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
1876 switch (DECL_FUNCTION_CODE (fndecl))
1878 case BUILT_IN_MALLOC:
1879 case BUILT_IN_CALLOC:
1880 case BUILT_IN_STRDUP:
1881 case BUILT_IN_STRNDUP:
1882 return true;
1883 default:
1884 break;
1887 return false;
1890 /* If PTR's nullness is not known, transition it to the "assumed-non-null"
1891 state for the current frame. */
1893 void
1894 malloc_state_machine::maybe_assume_non_null (sm_context *sm_ctxt,
1895 tree ptr,
1896 const gimple *stmt) const
1898 const region_model *old_model = sm_ctxt->get_old_region_model ();
1899 if (!old_model)
1900 return;
1902 tree null_ptr_cst = build_int_cst (TREE_TYPE (ptr), 0);
1903 tristate known_non_null
1904 = old_model->eval_condition (ptr, NE_EXPR, null_ptr_cst, NULL);
1905 if (known_non_null.is_unknown ())
1907 /* Cast away const-ness for cache-like operations. */
1908 malloc_state_machine *mut_this
1909 = const_cast <malloc_state_machine *> (this);
1910 state_t next_state
1911 = mut_this->get_or_create_assumed_non_null_state_for_frame
1912 (old_model->get_current_frame ());
1913 sm_ctxt->set_next_state (stmt, ptr, next_state);
1917 /* Implementation of state_machine::on_stmt vfunc for malloc_state_machine. */
1919 bool
1920 malloc_state_machine::on_stmt (sm_context *sm_ctxt,
1921 const supernode *node,
1922 const gimple *stmt) const
1924 if (const gcall *call = dyn_cast <const gcall *> (stmt))
1925 if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call))
1927 if (known_allocator_p (callee_fndecl, call))
1929 on_allocator_call (sm_ctxt, call, &m_free);
1930 return true;
1933 if (!is_placement_new_p (call))
1935 bool returns_nonnull = !TREE_NOTHROW (callee_fndecl)
1936 && flag_exceptions;
1937 if (is_named_call_p (callee_fndecl, "operator new"))
1938 on_allocator_call (sm_ctxt, call,
1939 &m_scalar_delete, returns_nonnull);
1940 else if (is_named_call_p (callee_fndecl, "operator new []"))
1941 on_allocator_call (sm_ctxt, call,
1942 &m_vector_delete, returns_nonnull);
1945 if (is_named_call_p (callee_fndecl, "operator delete", call, 1)
1946 || is_named_call_p (callee_fndecl, "operator delete", call, 2))
1948 on_deallocator_call (sm_ctxt, node, call,
1949 &m_scalar_delete.m_deallocator, 0);
1950 return true;
1952 else if (is_named_call_p (callee_fndecl, "operator delete []", call, 1))
1954 on_deallocator_call (sm_ctxt, node, call,
1955 &m_vector_delete.m_deallocator, 0);
1956 return true;
1959 if (is_named_call_p (callee_fndecl, "alloca", call, 1)
1960 || is_named_call_p (callee_fndecl, "__builtin_alloca", call, 1))
1962 tree lhs = gimple_call_lhs (call);
1963 if (lhs)
1964 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_non_heap);
1965 return true;
1968 if (is_named_call_p (callee_fndecl, "free", call, 1)
1969 || is_std_named_call_p (callee_fndecl, "free", call, 1)
1970 || is_named_call_p (callee_fndecl, "__builtin_free", call, 1))
1972 on_deallocator_call (sm_ctxt, node, call,
1973 &m_free.m_deallocator, 0);
1974 return true;
1977 if (is_named_call_p (callee_fndecl, "realloc", call, 2)
1978 || is_named_call_p (callee_fndecl, "__builtin_realloc", call, 2))
1980 on_realloc_call (sm_ctxt, node, call);
1981 return true;
1984 if (unaffected_by_call_p (callee_fndecl))
1985 return true;
1987 /* Cast away const-ness for cache-like operations. */
1988 malloc_state_machine *mutable_this
1989 = const_cast <malloc_state_machine *> (this);
1991 /* Handle interesting attributes of the callee_fndecl,
1992 or prioritize those of the builtin that callee_fndecl is expected
1993 to be.
1994 Might want this to be controlled by a flag. */
1996 tree fndecl = callee_fndecl;
1997 /* If call is recognized as a builtin known_function, use that
1998 builtin's function_decl. */
1999 if (const region_model *old_model = sm_ctxt->get_old_region_model ())
2000 if (const builtin_known_function *builtin_kf
2001 = old_model->get_builtin_kf (call))
2002 fndecl = builtin_kf->builtin_decl ();
2004 /* Handle "__attribute__((malloc(FOO)))". */
2005 if (const deallocator_set *deallocators
2006 = mutable_this->get_or_create_custom_deallocator_set
2007 (fndecl))
2009 tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (fndecl));
2010 bool returns_nonnull
2011 = lookup_attribute ("returns_nonnull", attrs);
2012 on_allocator_call (sm_ctxt, call, deallocators, returns_nonnull);
2016 /* Handle "__attribute__((nonnull))". */
2017 tree fntype = TREE_TYPE (fndecl);
2018 bitmap nonnull_args = get_nonnull_args (fntype);
2019 if (nonnull_args)
2021 for (unsigned i = 0; i < gimple_call_num_args (stmt); i++)
2023 tree arg = gimple_call_arg (stmt, i);
2024 if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
2025 continue;
2026 /* If we have a nonnull-args, and either all pointers, or
2027 just the specified pointers. */
2028 if (bitmap_empty_p (nonnull_args)
2029 || bitmap_bit_p (nonnull_args, i))
2031 state_t state = sm_ctxt->get_state (stmt, arg);
2032 /* Can't use a switch as the states are non-const. */
2033 /* Do use the fndecl that caused the warning so that the
2034 misused attributes are printed and the user not
2035 confused. */
2036 if (unchecked_p (state))
2038 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
2039 sm_ctxt->warn (node, stmt, arg,
2040 make_unique<possible_null_arg>
2041 (*this, diag_arg, fndecl, i));
2042 const allocation_state *astate
2043 = as_a_allocation_state (state);
2044 sm_ctxt->set_next_state (stmt, arg,
2045 astate->get_nonnull ());
2047 else if (state == m_null)
2049 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
2050 sm_ctxt->warn (node, stmt, arg,
2051 make_unique<null_arg>
2052 (*this, diag_arg, fndecl, i));
2053 sm_ctxt->set_next_state (stmt, arg, m_stop);
2055 else if (state == m_start)
2056 maybe_assume_non_null (sm_ctxt, arg, stmt);
2059 BITMAP_FREE (nonnull_args);
2063 /* Check for this after nonnull, so that if we have both
2064 then we transition to "freed", rather than "checked". */
2065 unsigned dealloc_argno = fndecl_dealloc_argno (fndecl);
2066 if (dealloc_argno != UINT_MAX)
2068 const deallocator *d
2069 = mutable_this->get_or_create_deallocator (fndecl);
2070 on_deallocator_call (sm_ctxt, node, call, d, dealloc_argno);
2075 /* Look for pointers explicitly being compared against zero
2076 that are in state assumed_non_null i.e. we already defererenced
2077 them.
2078 We have to do this check here, rather than in on_condition
2079 because we add a constraint that the pointer is non-null when
2080 dereferencing it, and this makes the apply_constraints_for_gcond
2081 find known-true and known-false conditions; on_condition is only
2082 called when adding new constraints. */
2083 if (const gcond *cond_stmt = dyn_cast <const gcond *> (stmt))
2085 enum tree_code op = gimple_cond_code (cond_stmt);
2086 if (op == EQ_EXPR || op == NE_EXPR)
2088 tree lhs = gimple_cond_lhs (cond_stmt);
2089 tree rhs = gimple_cond_rhs (cond_stmt);
2090 if (any_pointer_p (lhs)
2091 && any_pointer_p (rhs)
2092 && zerop (rhs))
2094 state_t state = sm_ctxt->get_state (stmt, lhs);
2095 if (assumed_non_null_p (state))
2096 maybe_complain_about_deref_before_check
2097 (sm_ctxt, node,
2098 stmt,
2099 (const assumed_non_null_state *)state,
2100 lhs);
2105 if (tree lhs = sm_ctxt->is_zero_assignment (stmt))
2106 if (any_pointer_p (lhs))
2107 on_zero_assignment (sm_ctxt, stmt,lhs);
2109 /* Handle dereferences. */
2110 for (unsigned i = 0; i < gimple_num_ops (stmt); i++)
2112 tree op = gimple_op (stmt, i);
2113 if (!op)
2114 continue;
2115 if (TREE_CODE (op) == COMPONENT_REF)
2116 op = TREE_OPERAND (op, 0);
2118 if (TREE_CODE (op) == MEM_REF)
2120 tree arg = TREE_OPERAND (op, 0);
2122 state_t state = sm_ctxt->get_state (stmt, arg);
2123 if (state == m_start)
2124 maybe_assume_non_null (sm_ctxt, arg, stmt);
2125 else if (unchecked_p (state))
2127 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
2128 sm_ctxt->warn (node, stmt, arg,
2129 make_unique<possible_null_deref> (*this,
2130 diag_arg));
2131 const allocation_state *astate = as_a_allocation_state (state);
2132 sm_ctxt->set_next_state (stmt, arg, astate->get_nonnull ());
2134 else if (state == m_null)
2136 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
2137 sm_ctxt->warn (node, stmt, arg,
2138 make_unique<null_deref> (*this, diag_arg));
2139 sm_ctxt->set_next_state (stmt, arg, m_stop);
2141 else if (freed_p (state))
2143 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
2144 const allocation_state *astate = as_a_allocation_state (state);
2145 sm_ctxt->warn (node, stmt, arg,
2146 make_unique<use_after_free>
2147 (*this, diag_arg, astate->m_deallocator));
2148 sm_ctxt->set_next_state (stmt, arg, m_stop);
2152 return false;
2155 /* Given a check against null of PTR in assumed-non-null state STATE,
2156 potentially add a deref_before_check warning to SM_CTXT. */
2158 void
2159 malloc_state_machine::
2160 maybe_complain_about_deref_before_check (sm_context *sm_ctxt,
2161 const supernode *node,
2162 const gimple *stmt,
2163 const assumed_non_null_state *state,
2164 tree ptr) const
2166 const region_model *model = sm_ctxt->get_old_region_model ();
2167 if (!model)
2168 return;
2170 /* Don't complain if the current frame (where the check is occurring) is
2171 deeper than the frame in which the "not null" assumption was made.
2172 This suppress false positives for cases like:
2174 void foo (struct s *p)
2176 int val = s->some_field; // deref here
2177 shared_helper (p);
2180 where "shared_helper" has:
2182 void shared_helper (struct s *p)
2184 if (!p) // check here
2185 return;
2186 // etc
2189 since the check in "shared_helper" is OK. */
2190 const frame_region *checked_in_frame = model->get_current_frame ();
2191 const frame_region *assumed_nonnull_in_frame = state->m_frame;
2192 if (checked_in_frame->get_index () > assumed_nonnull_in_frame->get_index ())
2193 return;
2195 tree diag_ptr = sm_ctxt->get_diagnostic_tree (ptr);
2196 if (diag_ptr)
2197 sm_ctxt->warn
2198 (node, stmt, ptr,
2199 make_unique<deref_before_check> (*this, diag_ptr));
2200 sm_ctxt->set_next_state (stmt, ptr, m_stop);
2203 /* Handle a call to an allocator.
2204 RETURNS_NONNULL is true if CALL is to a fndecl known to have
2205 __attribute__((returns_nonnull)). */
2207 void
2208 malloc_state_machine::on_allocator_call (sm_context *sm_ctxt,
2209 const gcall *call,
2210 const deallocator_set *deallocators,
2211 bool returns_nonnull) const
2213 tree lhs = gimple_call_lhs (call);
2214 if (lhs)
2216 if (sm_ctxt->get_state (call, lhs) == m_start)
2217 sm_ctxt->set_next_state (call, lhs,
2218 (returns_nonnull
2219 ? deallocators->m_nonnull
2220 : deallocators->m_unchecked));
2222 else
2224 /* TODO: report leak. */
2228 /* Handle deallocations of non-heap pointers.
2229 non-heap -> stop, with warning. */
2231 void
2232 malloc_state_machine::handle_free_of_non_heap (sm_context *sm_ctxt,
2233 const supernode *node,
2234 const gcall *call,
2235 tree arg,
2236 const deallocator *d) const
2238 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
2239 const region *freed_reg = NULL;
2240 if (const program_state *old_state = sm_ctxt->get_old_program_state ())
2242 const region_model *old_model = old_state->m_region_model;
2243 const svalue *ptr_sval = old_model->get_rvalue (arg, NULL);
2244 freed_reg = old_model->deref_rvalue (ptr_sval, arg, NULL);
2246 sm_ctxt->warn (node, call, arg,
2247 make_unique<free_of_non_heap>
2248 (*this, diag_arg, freed_reg, d->m_name));
2249 sm_ctxt->set_next_state (call, arg, m_stop);
2252 void
2253 malloc_state_machine::on_deallocator_call (sm_context *sm_ctxt,
2254 const supernode *node,
2255 const gcall *call,
2256 const deallocator *d,
2257 unsigned argno) const
2259 if (argno >= gimple_call_num_args (call))
2260 return;
2261 tree arg = gimple_call_arg (call, argno);
2263 state_t state = sm_ctxt->get_state (call, arg);
2265 /* start/assumed_non_null/unchecked/nonnull -> freed. */
2266 if (state == m_start || assumed_non_null_p (state))
2267 sm_ctxt->set_next_state (call, arg, d->m_freed);
2268 else if (unchecked_p (state) || nonnull_p (state))
2270 const allocation_state *astate = as_a_allocation_state (state);
2271 gcc_assert (astate->m_deallocators);
2272 if (!astate->m_deallocators->contains_p (d))
2274 /* Wrong allocator. */
2275 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
2276 sm_ctxt->warn (node, call, arg,
2277 make_unique<mismatching_deallocation>
2278 (*this, diag_arg,
2279 astate->m_deallocators,
2280 d));
2282 sm_ctxt->set_next_state (call, arg, d->m_freed);
2285 /* Keep state "null" as-is, rather than transitioning to "freed";
2286 we don't want to complain about double-free of NULL. */
2287 else if (state == d->m_freed)
2289 /* freed -> stop, with warning. */
2290 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
2291 sm_ctxt->warn (node, call, arg,
2292 make_unique<double_free> (*this, diag_arg, d->m_name));
2293 sm_ctxt->set_next_state (call, arg, m_stop);
2295 else if (state == m_non_heap)
2297 /* non-heap -> stop, with warning. */
2298 handle_free_of_non_heap (sm_ctxt, node, call, arg, d);
2302 /* Handle a call to "realloc".
2303 Check for free of non-heap or mismatching allocators,
2304 transitioning to the "stop" state for such cases.
2306 Otherwise, kf_realloc::impl_call_post will later
2307 get called (which will handle other sm-state transitions
2308 when the state is bifurcated). */
2310 void
2311 malloc_state_machine::on_realloc_call (sm_context *sm_ctxt,
2312 const supernode *node,
2313 const gcall *call) const
2315 const unsigned argno = 0;
2316 const deallocator *d = &m_realloc;
2318 tree arg = gimple_call_arg (call, argno);
2320 state_t state = sm_ctxt->get_state (call, arg);
2322 if (unchecked_p (state) || nonnull_p (state))
2324 const allocation_state *astate = as_a_allocation_state (state);
2325 gcc_assert (astate->m_deallocators);
2326 if (!astate->m_deallocators->contains_p (&m_free.m_deallocator))
2328 /* Wrong allocator. */
2329 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
2330 sm_ctxt->warn (node, call, arg,
2331 make_unique<mismatching_deallocation>
2332 (*this, diag_arg,
2333 astate->m_deallocators, d));
2334 sm_ctxt->set_next_state (call, arg, m_stop);
2335 if (path_context *path_ctxt = sm_ctxt->get_path_context ())
2336 path_ctxt->terminate_path ();
2339 else if (state == m_free.m_deallocator.m_freed)
2341 /* freed -> stop, with warning. */
2342 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
2343 sm_ctxt->warn (node, call, arg,
2344 make_unique<double_free> (*this, diag_arg, "free"));
2345 sm_ctxt->set_next_state (call, arg, m_stop);
2346 if (path_context *path_ctxt = sm_ctxt->get_path_context ())
2347 path_ctxt->terminate_path ();
2349 else if (state == m_non_heap)
2351 /* non-heap -> stop, with warning. */
2352 handle_free_of_non_heap (sm_ctxt, node, call, arg, d);
2353 if (path_context *path_ctxt = sm_ctxt->get_path_context ())
2354 path_ctxt->terminate_path ();
2358 /* Implementation of state_machine::on_phi vfunc for malloc_state_machine. */
2360 void
2361 malloc_state_machine::on_phi (sm_context *sm_ctxt,
2362 const supernode *node ATTRIBUTE_UNUSED,
2363 const gphi *phi,
2364 tree rhs) const
2366 if (zerop (rhs))
2368 tree lhs = gimple_phi_result (phi);
2369 on_zero_assignment (sm_ctxt, phi, lhs);
2373 /* Implementation of state_machine::on_condition vfunc for malloc_state_machine.
2374 Potentially transition state 'unchecked' to 'nonnull' or to 'null'. */
2376 void
2377 malloc_state_machine::on_condition (sm_context *sm_ctxt,
2378 const supernode *node ATTRIBUTE_UNUSED,
2379 const gimple *stmt,
2380 const svalue *lhs,
2381 enum tree_code op,
2382 const svalue *rhs) const
2384 if (!rhs->all_zeroes_p ())
2385 return;
2387 if (!any_pointer_p (lhs))
2388 return;
2389 if (!any_pointer_p (rhs))
2390 return;
2392 if (op == NE_EXPR)
2394 log ("got 'ARG != 0' match");
2395 state_t s = sm_ctxt->get_state (stmt, lhs);
2396 if (unchecked_p (s))
2398 const allocation_state *astate = as_a_allocation_state (s);
2399 sm_ctxt->set_next_state (stmt, lhs, astate->get_nonnull ());
2402 else if (op == EQ_EXPR)
2404 log ("got 'ARG == 0' match");
2405 state_t s = sm_ctxt->get_state (stmt, lhs);
2406 if (unchecked_p (s))
2407 sm_ctxt->set_next_state (stmt, lhs, m_null);
2411 /* Implementation of state_machine::on_pop_frame vfunc for malloc_state_machine.
2412 Clear any "assumed-non-null" state where the assumption happened in
2413 FRAME_REG. */
2415 void
2416 malloc_state_machine::on_pop_frame (sm_state_map *smap,
2417 const frame_region *frame_reg) const
2419 hash_set<const svalue *> svals_to_clear;
2420 for (auto kv : *smap)
2422 const svalue *sval = kv.first;
2423 state_t state = kv.second.m_state;
2424 if (assumed_non_null_p (state))
2426 const assumed_non_null_state *assumed_state
2427 = (const assumed_non_null_state *)state;
2428 if (frame_reg == assumed_state->m_frame)
2429 svals_to_clear.add (sval);
2432 for (auto sval : svals_to_clear)
2433 smap->clear_any_state (sval);
2436 /* Implementation of state_machine::can_purge_p vfunc for malloc_state_machine.
2437 Don't allow purging of pointers in state 'unchecked' or 'nonnull'
2438 (to avoid false leak reports). */
2440 bool
2441 malloc_state_machine::can_purge_p (state_t s) const
2443 enum resource_state rs = get_rs (s);
2444 return rs != RS_UNCHECKED && rs != RS_NONNULL;
2447 /* Implementation of state_machine::on_leak vfunc for malloc_state_machine
2448 (for complaining about leaks of pointers in state 'unchecked' and
2449 'nonnull'). */
2451 std::unique_ptr<pending_diagnostic>
2452 malloc_state_machine::on_leak (tree var) const
2454 return make_unique<malloc_leak> (*this, var);
2457 /* Implementation of state_machine::reset_when_passed_to_unknown_fn_p vfunc
2458 for malloc_state_machine. */
2460 bool
2461 malloc_state_machine::reset_when_passed_to_unknown_fn_p (state_t s,
2462 bool is_mutable) const
2464 /* An on-stack ptr doesn't stop being stack-allocated when passed to an
2465 unknown fn. */
2466 if (s == m_non_heap)
2467 return false;
2469 /* Otherwise, pointers passed as non-const can be freed. */
2470 return is_mutable;
2473 /* Implementation of state_machine::maybe_get_merged_states_nonequal vfunc
2474 for malloc_state_machine.
2476 Support discarding "assumed-non-null" states when merging with
2477 start state. */
2479 state_machine::state_t
2480 malloc_state_machine::maybe_get_merged_states_nonequal (state_t state_a,
2481 state_t state_b) const
2483 if (assumed_non_null_p (state_a) && state_b == m_start)
2484 return m_start;
2485 if (state_a == m_start && assumed_non_null_p (state_b))
2486 return m_start;
2487 return NULL;
2490 /* Return true if calls to FNDECL are known to not affect this sm-state. */
2492 bool
2493 malloc_state_machine::unaffected_by_call_p (tree fndecl)
2495 /* A set of functions that are known to not affect allocation
2496 status, even if we haven't fully modelled the rest of their
2497 behavior yet. */
2498 static const char * const funcnames[] = {
2499 /* This array must be kept sorted. */
2500 "strsep",
2502 const size_t count = ARRAY_SIZE (funcnames);
2503 function_set fs (funcnames, count);
2505 if (fs.contains_decl_p (fndecl))
2506 return true;
2508 return false;
2511 /* Shared logic for handling GIMPLE_ASSIGNs and GIMPLE_PHIs that
2512 assign zero to LHS. */
2514 void
2515 malloc_state_machine::on_zero_assignment (sm_context *sm_ctxt,
2516 const gimple *stmt,
2517 tree lhs) const
2519 state_t s = sm_ctxt->get_state (stmt, lhs);
2520 enum resource_state rs = get_rs (s);
2521 if (rs == RS_START
2522 || rs == RS_UNCHECKED
2523 || rs == RS_NONNULL
2524 || rs == RS_FREED)
2525 sm_ctxt->set_next_state (stmt, lhs, m_null);
2528 /* Special-case hook for handling realloc, for the "success with move to
2529 a new buffer" case, marking OLD_PTR_SVAL as freed and NEW_PTR_SVAL as
2530 non-null.
2532 This is similar to on_deallocator_call and on_allocator_call,
2533 but the checks happen in on_realloc_call, and by splitting the states. */
2535 void
2536 malloc_state_machine::
2537 on_realloc_with_move (region_model *model,
2538 sm_state_map *smap,
2539 const svalue *old_ptr_sval,
2540 const svalue *new_ptr_sval,
2541 const extrinsic_state &ext_state) const
2543 smap->set_state (model, old_ptr_sval,
2544 m_free.m_deallocator.m_freed,
2545 NULL, ext_state);
2547 smap->set_state (model, new_ptr_sval,
2548 m_free.m_nonnull,
2549 NULL, ext_state);
2552 /* Hook for get_or_create_region_for_heap_alloc for the case when we want
2553 ptr_sval to mark a newly created region as assumed non null on malloc SM. */
2554 void
2555 malloc_state_machine::transition_ptr_sval_non_null (region_model *model,
2556 sm_state_map *smap,
2557 const svalue *new_ptr_sval,
2558 const extrinsic_state &ext_state) const
2560 smap->set_state (model, new_ptr_sval, m_free.m_nonnull, NULL, ext_state);
2563 } // anonymous namespace
2565 /* Internal interface to this file. */
2567 state_machine *
2568 make_malloc_state_machine (logger *logger)
2570 return new malloc_state_machine (logger);
2573 /* Specialcase hook for handling realloc, for use by
2574 kf_realloc::impl_call_post::success_with_move::update_model. */
2576 void
2577 region_model::on_realloc_with_move (const call_details &cd,
2578 const svalue *old_ptr_sval,
2579 const svalue *new_ptr_sval)
2581 region_model_context *ctxt = cd.get_ctxt ();
2582 if (!ctxt)
2583 return;
2584 const extrinsic_state *ext_state = ctxt->get_ext_state ();
2585 if (!ext_state)
2586 return;
2588 sm_state_map *smap;
2589 const state_machine *sm;
2590 unsigned sm_idx;
2591 if (!ctxt->get_malloc_map (&smap, &sm, &sm_idx))
2592 return;
2594 gcc_assert (smap);
2595 gcc_assert (sm);
2597 const malloc_state_machine &malloc_sm
2598 = (const malloc_state_machine &)*sm;
2600 malloc_sm.on_realloc_with_move (this,
2601 smap,
2602 old_ptr_sval,
2603 new_ptr_sval,
2604 *ext_state);
2607 /* Moves ptr_sval from start to assumed non-null, for use by
2608 region_model::get_or_create_region_for_heap_alloc. */
2609 void
2610 region_model::transition_ptr_sval_non_null (region_model_context *ctxt,
2611 const svalue *ptr_sval)
2613 if (!ctxt)
2614 return;
2615 const extrinsic_state *ext_state = ctxt->get_ext_state ();
2616 if (!ext_state)
2617 return;
2619 sm_state_map *smap;
2620 const state_machine *sm;
2621 unsigned sm_idx;
2622 if (!ctxt->get_malloc_map (&smap, &sm, &sm_idx))
2623 return;
2625 gcc_assert (smap);
2626 gcc_assert (sm);
2628 const malloc_state_machine &malloc_sm = (const malloc_state_machine &)*sm;
2630 malloc_sm.transition_ptr_sval_non_null (this, smap, ptr_sval, *ext_state);
2633 } // namespace ana
2635 #endif /* #if ENABLE_ANALYZER */