d: Add test for PR d/108167 to the testsuite [PR108167]
[official-gcc.git] / gcc / analyzer / sm-malloc.cc
blob1ea9b30fa13d5594c627599b9b2e53e3b863fb21
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 standard_deallocator_set m_free;
438 standard_deallocator_set m_scalar_delete;
439 standard_deallocator_set m_vector_delete;
441 standard_deallocator m_realloc;
443 /* States that are independent of api. */
445 /* States for a pointer that's been unconditionally dereferenced
446 in a particular stack frame. */
447 hash_map<const frame_region *, state_t> m_assumed_non_null;
449 /* State for a pointer that's known to be NULL. */
450 state_t m_null;
452 /* State for a pointer that's known to not be on the heap (e.g. to a local
453 or global). */
454 state_t m_non_heap; // TODO: or should this be a different state machine?
455 // or do we need child values etc?
457 /* Stop state, for pointers we don't want to track any more. */
458 state_t m_stop;
460 private:
461 const custom_deallocator_set *
462 get_or_create_custom_deallocator_set (tree allocator_fndecl);
463 custom_deallocator_set *
464 maybe_create_custom_deallocator_set (tree allocator_fndecl);
465 const deallocator *
466 get_or_create_deallocator (tree deallocator_fndecl);
468 state_t
469 get_or_create_assumed_non_null_state_for_frame (const frame_region *frame);
471 void
472 maybe_complain_about_deref_before_check (sm_context *sm_ctxt,
473 const supernode *node,
474 const gimple *stmt,
475 const assumed_non_null_state *,
476 tree ptr) const;
478 void on_allocator_call (sm_context *sm_ctxt,
479 const gcall *call,
480 const deallocator_set *deallocators,
481 bool returns_nonnull = false) const;
482 void handle_free_of_non_heap (sm_context *sm_ctxt,
483 const supernode *node,
484 const gcall *call,
485 tree arg,
486 const deallocator *d) const;
487 void on_deallocator_call (sm_context *sm_ctxt,
488 const supernode *node,
489 const gcall *call,
490 const deallocator *d,
491 unsigned argno) const;
492 void on_realloc_call (sm_context *sm_ctxt,
493 const supernode *node,
494 const gcall *call) const;
495 void on_zero_assignment (sm_context *sm_ctxt,
496 const gimple *stmt,
497 tree lhs) const;
499 /* A map for consolidating deallocators so that they are
500 unique per deallocator FUNCTION_DECL. */
501 typedef hash_map<tree, deallocator *> deallocator_map_t;
502 deallocator_map_t m_deallocator_map;
504 /* Memoized lookups from FUNCTION_DECL to custom_deallocator_set *. */
505 typedef hash_map<tree, custom_deallocator_set *> deallocator_set_cache_t;
506 deallocator_set_cache_t m_custom_deallocator_set_cache;
508 /* A map for consolidating custom_deallocator_set instances. */
509 typedef hash_map<custom_deallocator_set::key_t,
510 custom_deallocator_set *,
511 deallocator_set_map_traits> custom_deallocator_set_map_t;
512 custom_deallocator_set_map_t m_custom_deallocator_set_map;
514 /* Record of dynamically-allocated objects, for cleanup. */
515 auto_vec <custom_deallocator_set *> m_dynamic_sets;
516 auto_vec <custom_deallocator *> m_dynamic_deallocators;
519 /* struct deallocator. */
521 deallocator::deallocator (malloc_state_machine *sm,
522 const char *name,
523 enum wording wording)
524 : m_name (name),
525 m_wording (wording),
526 m_freed (sm->add_state ("freed", RS_FREED, NULL, this))
530 hashval_t
531 deallocator::hash () const
533 return (hashval_t)m_freed->get_id ();
536 void
537 deallocator::dump_to_pp (pretty_printer *pp) const
539 pp_printf (pp, "%qs", m_name);
543 deallocator::cmp (const deallocator *a, const deallocator *b)
545 return (int)a->m_freed->get_id () - (int)b->m_freed->get_id ();
549 deallocator::cmp_ptr_ptr (const void *a, const void *b)
551 return cmp (*(const deallocator * const *)a,
552 *(const deallocator * const *)b);
556 /* struct standard_deallocator : public deallocator. */
558 standard_deallocator::standard_deallocator (malloc_state_machine *sm,
559 const char *name,
560 enum wording wording)
561 : deallocator (sm, name, wording)
565 /* struct deallocator_set. */
567 deallocator_set::deallocator_set (malloc_state_machine *sm,
568 enum wording wording)
569 : m_wording (wording),
570 m_unchecked (sm->add_state ("unchecked", RS_UNCHECKED, this, NULL)),
571 m_nonnull (sm->add_state ("nonnull", RS_NONNULL, this, NULL))
575 /* Dump a description of this deallocator_set to stderr. */
577 DEBUG_FUNCTION void
578 deallocator_set::dump () const
580 pretty_printer pp;
581 pp_show_color (&pp) = pp_show_color (global_dc->printer);
582 pp.buffer->stream = stderr;
583 dump_to_pp (&pp);
584 pp_newline (&pp);
585 pp_flush (&pp);
588 /* struct custom_deallocator_set : public deallocator_set. */
590 custom_deallocator_set::
591 custom_deallocator_set (malloc_state_machine *sm,
592 const auto_vec <const deallocator *> *vec,
593 enum wording wording)
594 : deallocator_set (sm, wording),
595 m_deallocator_vec (vec->length ())
597 unsigned i;
598 const deallocator *d;
599 FOR_EACH_VEC_ELT (*vec, i, d)
600 m_deallocator_vec.safe_push (d);
603 bool
604 custom_deallocator_set::contains_p (const deallocator *d) const
606 unsigned i;
607 const deallocator *cd;
608 FOR_EACH_VEC_ELT (m_deallocator_vec, i, cd)
609 if (cd == d)
610 return true;
611 return false;
614 const deallocator *
615 custom_deallocator_set::maybe_get_single () const
617 if (m_deallocator_vec.length () == 1)
618 return m_deallocator_vec[0];
619 return NULL;
622 void
623 custom_deallocator_set::dump_to_pp (pretty_printer *pp) const
625 pp_character (pp, '{');
626 unsigned i;
627 const deallocator *d;
628 FOR_EACH_VEC_ELT (m_deallocator_vec, i, d)
630 if (i > 0)
631 pp_string (pp, ", ");
632 d->dump_to_pp (pp);
634 pp_character (pp, '}');
637 /* struct standard_deallocator_set : public deallocator_set. */
639 standard_deallocator_set::standard_deallocator_set (malloc_state_machine *sm,
640 const char *name,
641 enum wording wording)
642 : deallocator_set (sm, wording),
643 m_deallocator (sm, name, wording)
647 bool
648 standard_deallocator_set::contains_p (const deallocator *d) const
650 return d == &m_deallocator;
653 const deallocator *
654 standard_deallocator_set::maybe_get_single () const
656 return &m_deallocator;
659 void
660 standard_deallocator_set::dump_to_pp (pretty_printer *pp) const
662 pp_character (pp, '{');
663 pp_string (pp, m_deallocator.m_name);
664 pp_character (pp, '}');
667 /* Return STATE cast to the custom state subclass, or NULL for the start state.
668 Everything should be an allocation_state apart from the start state. */
670 static const allocation_state *
671 dyn_cast_allocation_state (state_machine::state_t state)
673 if (state->get_id () == 0)
674 return NULL;
675 return static_cast <const allocation_state *> (state);
678 /* Return STATE cast to the custom state subclass, for a state that is
679 already known to not be the start state . */
681 static const allocation_state *
682 as_a_allocation_state (state_machine::state_t state)
684 gcc_assert (state->get_id () != 0);
685 return static_cast <const allocation_state *> (state);
688 /* Get the resource_state for STATE. */
690 static enum resource_state
691 get_rs (state_machine::state_t state)
693 if (const allocation_state *astate = dyn_cast_allocation_state (state))
694 return astate->m_rs;
695 else
696 return RS_START;
699 /* Return true if STATE is the start state. */
701 static bool
702 start_p (state_machine::state_t state)
704 return get_rs (state) == RS_START;
707 /* Return true if STATE is an unchecked result from an allocator. */
709 static bool
710 unchecked_p (state_machine::state_t state)
712 return get_rs (state) == RS_UNCHECKED;
715 /* Return true if STATE is a non-null result from an allocator. */
717 static bool
718 nonnull_p (state_machine::state_t state)
720 return get_rs (state) == RS_NONNULL;
723 /* Return true if STATE is a value that has been passed to a deallocator. */
725 static bool
726 freed_p (state_machine::state_t state)
728 return get_rs (state) == RS_FREED;
731 /* Return true if STATE is a value that has been assumed to be non-NULL. */
733 static bool
734 assumed_non_null_p (state_machine::state_t state)
736 return get_rs (state) == RS_ASSUMED_NON_NULL;
739 /* Class for diagnostics relating to malloc_state_machine. */
741 class malloc_diagnostic : public pending_diagnostic
743 public:
744 malloc_diagnostic (const malloc_state_machine &sm, tree arg)
745 : m_sm (sm), m_arg (arg)
748 bool subclass_equal_p (const pending_diagnostic &base_other) const override
750 return same_tree_p (m_arg, ((const malloc_diagnostic &)base_other).m_arg);
753 label_text describe_state_change (const evdesc::state_change &change)
754 override
756 if (change.m_old_state == m_sm.get_start_state ()
757 && unchecked_p (change.m_new_state))
758 // TODO: verify that it's the allocation stmt, not a copy
759 return label_text::borrow ("allocated here");
760 if (unchecked_p (change.m_old_state)
761 && nonnull_p (change.m_new_state))
763 if (change.m_expr)
764 return change.formatted_print ("assuming %qE is non-NULL",
765 change.m_expr);
766 else
767 return change.formatted_print ("assuming %qs is non-NULL",
768 "<unknown>");
770 if (change.m_new_state == m_sm.m_null)
772 if (unchecked_p (change.m_old_state))
774 if (change.m_expr)
775 return change.formatted_print ("assuming %qE is NULL",
776 change.m_expr);
777 else
778 return change.formatted_print ("assuming %qs is NULL",
779 "<unknown>");
781 else
783 if (change.m_expr)
784 return change.formatted_print ("%qE is NULL",
785 change.m_expr);
786 else
787 return change.formatted_print ("%qs is NULL",
788 "<unknown>");
792 return label_text ();
795 diagnostic_event::meaning
796 get_meaning_for_state_change (const evdesc::state_change &change)
797 const final override
799 if (change.m_old_state == m_sm.get_start_state ()
800 && unchecked_p (change.m_new_state))
801 return diagnostic_event::meaning (diagnostic_event::VERB_acquire,
802 diagnostic_event::NOUN_memory);
803 if (freed_p (change.m_new_state))
804 return diagnostic_event::meaning (diagnostic_event::VERB_release,
805 diagnostic_event::NOUN_memory);
806 return diagnostic_event::meaning ();
809 protected:
810 const malloc_state_machine &m_sm;
811 tree m_arg;
814 /* Concrete subclass for reporting mismatching allocator/deallocator
815 diagnostics. */
817 class mismatching_deallocation : public malloc_diagnostic
819 public:
820 mismatching_deallocation (const malloc_state_machine &sm, tree arg,
821 const deallocator_set *expected_deallocators,
822 const deallocator *actual_dealloc)
823 : malloc_diagnostic (sm, arg),
824 m_expected_deallocators (expected_deallocators),
825 m_actual_dealloc (actual_dealloc)
828 const char *get_kind () const final override
830 return "mismatching_deallocation";
833 int get_controlling_option () const final override
835 return OPT_Wanalyzer_mismatching_deallocation;
838 bool emit (rich_location *rich_loc) final override
840 auto_diagnostic_group d;
841 diagnostic_metadata m;
842 m.add_cwe (762); /* CWE-762: Mismatched Memory Management Routines. */
843 if (const deallocator *expected_dealloc
844 = m_expected_deallocators->maybe_get_single ())
845 return warning_meta (rich_loc, m, get_controlling_option (),
846 "%qE should have been deallocated with %qs"
847 " but was deallocated with %qs",
848 m_arg, expected_dealloc->m_name,
849 m_actual_dealloc->m_name);
850 else
851 return warning_meta (rich_loc, m, get_controlling_option (),
852 "%qs called on %qE returned from a mismatched"
853 " allocation function",
854 m_actual_dealloc->m_name, m_arg);
857 label_text describe_state_change (const evdesc::state_change &change)
858 final override
860 if (unchecked_p (change.m_new_state))
862 m_alloc_event = change.m_event_id;
863 if (const deallocator *expected_dealloc
864 = m_expected_deallocators->maybe_get_single ())
865 return change.formatted_print ("allocated here"
866 " (expects deallocation with %qs)",
867 expected_dealloc->m_name);
868 else
869 return change.formatted_print ("allocated here");
871 return malloc_diagnostic::describe_state_change (change);
874 label_text describe_final_event (const evdesc::final_event &ev) final override
876 if (m_alloc_event.known_p ())
878 if (const deallocator *expected_dealloc
879 = m_expected_deallocators->maybe_get_single ())
880 return ev.formatted_print
881 ("deallocated with %qs here;"
882 " allocation at %@ expects deallocation with %qs",
883 m_actual_dealloc->m_name, &m_alloc_event,
884 expected_dealloc->m_name);
885 else
886 return ev.formatted_print
887 ("deallocated with %qs here;"
888 " allocated at %@",
889 m_actual_dealloc->m_name, &m_alloc_event);
891 return ev.formatted_print ("deallocated with %qs here",
892 m_actual_dealloc->m_name);
895 private:
896 diagnostic_event_id_t m_alloc_event;
897 const deallocator_set *m_expected_deallocators;
898 const deallocator *m_actual_dealloc;
901 /* Concrete subclass for reporting double-free diagnostics. */
903 class double_free : public malloc_diagnostic
905 public:
906 double_free (const malloc_state_machine &sm, tree arg, const char *funcname)
907 : malloc_diagnostic (sm, arg), m_funcname (funcname)
910 const char *get_kind () const final override { return "double_free"; }
912 int get_controlling_option () const final override
914 return OPT_Wanalyzer_double_free;
917 bool emit (rich_location *rich_loc) final override
919 auto_diagnostic_group d;
920 diagnostic_metadata m;
921 m.add_cwe (415); /* CWE-415: Double Free. */
922 return warning_meta (rich_loc, m, get_controlling_option (),
923 "double-%qs of %qE", m_funcname, m_arg);
926 label_text describe_state_change (const evdesc::state_change &change)
927 final override
929 if (freed_p (change.m_new_state))
931 m_first_free_event = change.m_event_id;
932 return change.formatted_print ("first %qs here", m_funcname);
934 return malloc_diagnostic::describe_state_change (change);
937 label_text describe_call_with_state (const evdesc::call_with_state &info)
938 final override
940 if (freed_p (info.m_state))
941 return info.formatted_print
942 ("passing freed pointer %qE in call to %qE from %qE",
943 info.m_expr, info.m_callee_fndecl, info.m_caller_fndecl);
944 return label_text ();
947 label_text describe_final_event (const evdesc::final_event &ev) final override
949 if (m_first_free_event.known_p ())
950 return ev.formatted_print ("second %qs here; first %qs was at %@",
951 m_funcname, m_funcname,
952 &m_first_free_event);
953 return ev.formatted_print ("second %qs here", m_funcname);
956 private:
957 diagnostic_event_id_t m_first_free_event;
958 const char *m_funcname;
961 /* Abstract subclass for describing possible bad uses of NULL.
962 Responsible for describing the call that could return NULL. */
964 class possible_null : public malloc_diagnostic
966 public:
967 possible_null (const malloc_state_machine &sm, tree arg)
968 : malloc_diagnostic (sm, arg)
971 label_text describe_state_change (const evdesc::state_change &change)
972 final override
974 if (change.m_old_state == m_sm.get_start_state ()
975 && unchecked_p (change.m_new_state))
977 m_origin_of_unchecked_event = change.m_event_id;
978 return label_text::borrow ("this call could return NULL");
980 return malloc_diagnostic::describe_state_change (change);
983 label_text describe_return_of_state (const evdesc::return_of_state &info)
984 final override
986 if (unchecked_p (info.m_state))
987 return info.formatted_print ("possible return of NULL to %qE from %qE",
988 info.m_caller_fndecl, info.m_callee_fndecl);
989 return label_text ();
992 protected:
993 diagnostic_event_id_t m_origin_of_unchecked_event;
996 /* Concrete subclass for describing dereference of a possible NULL
997 value. */
999 class possible_null_deref : public possible_null
1001 public:
1002 possible_null_deref (const malloc_state_machine &sm, tree arg)
1003 : possible_null (sm, arg)
1006 const char *get_kind () const final override { return "possible_null_deref"; }
1008 int get_controlling_option () const final override
1010 return OPT_Wanalyzer_possible_null_dereference;
1013 bool emit (rich_location *rich_loc) final override
1015 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
1016 diagnostic_metadata m;
1017 m.add_cwe (690);
1018 return warning_meta (rich_loc, m, get_controlling_option (),
1019 "dereference of possibly-NULL %qE", m_arg);
1022 label_text describe_final_event (const evdesc::final_event &ev) final override
1024 if (m_origin_of_unchecked_event.known_p ())
1025 return ev.formatted_print ("%qE could be NULL: unchecked value from %@",
1026 ev.m_expr,
1027 &m_origin_of_unchecked_event);
1028 else
1029 return ev.formatted_print ("%qE could be NULL", ev.m_expr);
1034 /* Return true if FNDECL is a C++ method. */
1036 static bool
1037 method_p (tree fndecl)
1039 return TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE;
1042 /* Return a 1-based description of ARG_IDX (0-based) of FNDECL.
1043 Compare with %P in the C++ FE (implemented in cp/error.cc: parm_to_string
1044 as called from cp_printer). */
1046 static label_text
1047 describe_argument_index (tree fndecl, int arg_idx)
1049 if (method_p (fndecl))
1050 if (arg_idx == 0)
1051 return label_text::borrow ("'this'");
1052 pretty_printer pp;
1053 pp_printf (&pp, "%u", arg_idx + 1 - method_p (fndecl));
1054 return label_text::take (xstrdup (pp_formatted_text (&pp)));
1057 /* Subroutine for use by possible_null_arg::emit and null_arg::emit.
1058 Issue a note informing that the pertinent argument must be non-NULL. */
1060 static void
1061 inform_nonnull_attribute (tree fndecl, int arg_idx)
1063 label_text arg_desc = describe_argument_index (fndecl, arg_idx);
1064 inform (DECL_SOURCE_LOCATION (fndecl),
1065 "argument %s of %qD must be non-null",
1066 arg_desc.get (), fndecl);
1067 /* Ideally we would use the location of the parm and underline the
1068 attribute also - but we don't have the location_t values at this point
1069 in the middle-end.
1070 For reference, the C and C++ FEs have get_fndecl_argument_location. */
1073 /* Concrete subclass for describing passing a possibly-NULL value to a
1074 function marked with __attribute__((nonnull)). */
1076 class possible_null_arg : public possible_null
1078 public:
1079 possible_null_arg (const malloc_state_machine &sm, tree arg,
1080 tree fndecl, int arg_idx)
1081 : possible_null (sm, arg),
1082 m_fndecl (fndecl), m_arg_idx (arg_idx)
1085 const char *get_kind () const final override { return "possible_null_arg"; }
1087 bool subclass_equal_p (const pending_diagnostic &base_other)
1088 const final override
1090 const possible_null_arg &sub_other
1091 = (const possible_null_arg &)base_other;
1092 return (same_tree_p (m_arg, sub_other.m_arg)
1093 && m_fndecl == sub_other.m_fndecl
1094 && m_arg_idx == sub_other.m_arg_idx);
1097 int get_controlling_option () const final override
1099 return OPT_Wanalyzer_possible_null_argument;
1102 bool emit (rich_location *rich_loc) final override
1104 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
1105 auto_diagnostic_group d;
1106 diagnostic_metadata m;
1107 m.add_cwe (690);
1108 bool warned
1109 = warning_meta (rich_loc, m, get_controlling_option (),
1110 "use of possibly-NULL %qE where non-null expected",
1111 m_arg);
1112 if (warned)
1113 inform_nonnull_attribute (m_fndecl, m_arg_idx);
1114 return warned;
1117 label_text describe_final_event (const evdesc::final_event &ev) final override
1119 label_text arg_desc = describe_argument_index (m_fndecl, m_arg_idx);
1120 label_text result;
1121 if (m_origin_of_unchecked_event.known_p ())
1122 result = ev.formatted_print ("argument %s (%qE) from %@ could be NULL"
1123 " where non-null expected",
1124 arg_desc.get (), ev.m_expr,
1125 &m_origin_of_unchecked_event);
1126 else
1127 result = ev.formatted_print ("argument %s (%qE) could be NULL"
1128 " where non-null expected",
1129 arg_desc.get (), ev.m_expr);
1130 return result;
1133 private:
1134 tree m_fndecl;
1135 int m_arg_idx;
1138 /* Concrete subclass for describing a dereference of a NULL value. */
1140 class null_deref : public malloc_diagnostic
1142 public:
1143 null_deref (const malloc_state_machine &sm, tree arg)
1144 : malloc_diagnostic (sm, arg) {}
1146 const char *get_kind () const final override { return "null_deref"; }
1148 int get_controlling_option () const final override
1150 return OPT_Wanalyzer_null_dereference;
1153 bool terminate_path_p () const final override { return true; }
1155 bool emit (rich_location *rich_loc) final override
1157 /* CWE-476: NULL Pointer Dereference. */
1158 diagnostic_metadata m;
1159 m.add_cwe (476);
1160 return warning_meta (rich_loc, m, get_controlling_option (),
1161 "dereference of NULL %qE", m_arg);
1164 label_text describe_return_of_state (const evdesc::return_of_state &info)
1165 final override
1167 if (info.m_state == m_sm.m_null)
1168 return info.formatted_print ("return of NULL to %qE from %qE",
1169 info.m_caller_fndecl, info.m_callee_fndecl);
1170 return label_text ();
1173 label_text describe_final_event (const evdesc::final_event &ev) final override
1175 return ev.formatted_print ("dereference of NULL %qE", ev.m_expr);
1179 /* Concrete subclass for describing passing a NULL value to a
1180 function marked with __attribute__((nonnull)). */
1182 class null_arg : public malloc_diagnostic
1184 public:
1185 null_arg (const malloc_state_machine &sm, tree arg,
1186 tree fndecl, int arg_idx)
1187 : malloc_diagnostic (sm, arg),
1188 m_fndecl (fndecl), m_arg_idx (arg_idx)
1191 const char *get_kind () const final override { return "null_arg"; }
1193 bool subclass_equal_p (const pending_diagnostic &base_other)
1194 const final override
1196 const null_arg &sub_other
1197 = (const null_arg &)base_other;
1198 return (same_tree_p (m_arg, sub_other.m_arg)
1199 && m_fndecl == sub_other.m_fndecl
1200 && m_arg_idx == sub_other.m_arg_idx);
1203 int get_controlling_option () const final override
1205 return OPT_Wanalyzer_null_argument;
1208 bool terminate_path_p () const final override { return true; }
1210 bool emit (rich_location *rich_loc) final override
1212 /* CWE-476: NULL Pointer Dereference. */
1213 auto_diagnostic_group d;
1214 diagnostic_metadata m;
1215 m.add_cwe (476);
1217 bool warned;
1218 if (zerop (m_arg))
1219 warned = warning_meta (rich_loc, m, get_controlling_option (),
1220 "use of NULL where non-null expected");
1221 else
1222 warned = warning_meta (rich_loc, m, get_controlling_option (),
1223 "use of NULL %qE where non-null expected",
1224 m_arg);
1225 if (warned)
1226 inform_nonnull_attribute (m_fndecl, m_arg_idx);
1227 return warned;
1230 label_text describe_final_event (const evdesc::final_event &ev) final override
1232 label_text arg_desc = describe_argument_index (m_fndecl, m_arg_idx);
1233 label_text result;
1234 if (zerop (ev.m_expr))
1235 result = ev.formatted_print ("argument %s NULL where non-null expected",
1236 arg_desc.get ());
1237 else
1238 result = ev.formatted_print ("argument %s (%qE) NULL"
1239 " where non-null expected",
1240 arg_desc.get (), ev.m_expr);
1241 return result;
1244 private:
1245 tree m_fndecl;
1246 int m_arg_idx;
1249 class use_after_free : public malloc_diagnostic
1251 public:
1252 use_after_free (const malloc_state_machine &sm, tree arg,
1253 const deallocator *deallocator)
1254 : malloc_diagnostic (sm, arg),
1255 m_deallocator (deallocator)
1257 gcc_assert (deallocator);
1260 const char *get_kind () const final override { return "use_after_free"; }
1262 int get_controlling_option () const final override
1264 return OPT_Wanalyzer_use_after_free;
1267 bool emit (rich_location *rich_loc) final override
1269 /* CWE-416: Use After Free. */
1270 diagnostic_metadata m;
1271 m.add_cwe (416);
1272 return warning_meta (rich_loc, m, get_controlling_option (),
1273 "use after %<%s%> of %qE",
1274 m_deallocator->m_name, m_arg);
1277 label_text describe_state_change (const evdesc::state_change &change)
1278 final override
1280 if (freed_p (change.m_new_state))
1282 m_free_event = change.m_event_id;
1283 switch (m_deallocator->m_wording)
1285 default:
1286 case WORDING_REALLOCATED:
1287 gcc_unreachable ();
1288 case WORDING_FREED:
1289 return label_text::borrow ("freed here");
1290 case WORDING_DELETED:
1291 return label_text::borrow ("deleted here");
1292 case WORDING_DEALLOCATED:
1293 return label_text::borrow ("deallocated here");
1296 return malloc_diagnostic::describe_state_change (change);
1299 label_text describe_final_event (const evdesc::final_event &ev) final override
1301 const char *funcname = m_deallocator->m_name;
1302 if (m_free_event.known_p ())
1303 switch (m_deallocator->m_wording)
1305 default:
1306 case WORDING_REALLOCATED:
1307 gcc_unreachable ();
1308 case WORDING_FREED:
1309 return ev.formatted_print ("use after %<%s%> of %qE; freed at %@",
1310 funcname, ev.m_expr, &m_free_event);
1311 case WORDING_DELETED:
1312 return ev.formatted_print ("use after %<%s%> of %qE; deleted at %@",
1313 funcname, ev.m_expr, &m_free_event);
1314 case WORDING_DEALLOCATED:
1315 return ev.formatted_print ("use after %<%s%> of %qE;"
1316 " deallocated at %@",
1317 funcname, ev.m_expr, &m_free_event);
1319 else
1320 return ev.formatted_print ("use after %<%s%> of %qE",
1321 funcname, ev.m_expr);
1324 /* Implementation of pending_diagnostic::supercedes_p for
1325 use_after_free.
1327 We want use-after-free to supercede use-of-unitialized-value,
1328 so that if we have these at the same stmt, we don't emit
1329 a use-of-uninitialized, just the use-after-free.
1330 (this is because we fully purge information about freed
1331 buffers when we free them to avoid state explosions, so
1332 that if they are accessed after the free, it looks like
1333 they are uninitialized). */
1335 bool supercedes_p (const pending_diagnostic &other) const final override
1337 if (other.use_of_uninit_p ())
1338 return true;
1340 return false;
1343 private:
1344 diagnostic_event_id_t m_free_event;
1345 const deallocator *m_deallocator;
1348 class malloc_leak : public malloc_diagnostic
1350 public:
1351 malloc_leak (const malloc_state_machine &sm, tree arg)
1352 : malloc_diagnostic (sm, arg) {}
1354 const char *get_kind () const final override { return "malloc_leak"; }
1356 int get_controlling_option () const final override
1358 return OPT_Wanalyzer_malloc_leak;
1361 bool emit (rich_location *rich_loc) final override
1363 /* "CWE-401: Missing Release of Memory after Effective Lifetime". */
1364 diagnostic_metadata m;
1365 m.add_cwe (401);
1366 if (m_arg)
1367 return warning_meta (rich_loc, m, get_controlling_option (),
1368 "leak of %qE", m_arg);
1369 else
1370 return warning_meta (rich_loc, m, get_controlling_option (),
1371 "leak of %qs", "<unknown>");
1374 label_text describe_state_change (const evdesc::state_change &change)
1375 final override
1377 if (unchecked_p (change.m_new_state)
1378 || (start_p (change.m_old_state) && nonnull_p (change.m_new_state)))
1380 m_alloc_event = change.m_event_id;
1381 return label_text::borrow ("allocated here");
1383 return malloc_diagnostic::describe_state_change (change);
1386 label_text describe_final_event (const evdesc::final_event &ev) final override
1388 if (ev.m_expr)
1390 if (m_alloc_event.known_p ())
1391 return ev.formatted_print ("%qE leaks here; was allocated at %@",
1392 ev.m_expr, &m_alloc_event);
1393 else
1394 return ev.formatted_print ("%qE leaks here", ev.m_expr);
1396 else
1398 if (m_alloc_event.known_p ())
1399 return ev.formatted_print ("%qs leaks here; was allocated at %@",
1400 "<unknown>", &m_alloc_event);
1401 else
1402 return ev.formatted_print ("%qs leaks here", "<unknown>");
1406 private:
1407 diagnostic_event_id_t m_alloc_event;
1410 class free_of_non_heap : public malloc_diagnostic
1412 public:
1413 free_of_non_heap (const malloc_state_machine &sm, tree arg,
1414 const region *freed_reg,
1415 const char *funcname)
1416 : malloc_diagnostic (sm, arg), m_freed_reg (freed_reg), m_funcname (funcname)
1420 const char *get_kind () const final override { return "free_of_non_heap"; }
1422 bool subclass_equal_p (const pending_diagnostic &base_other) const
1423 final override
1425 const free_of_non_heap &other = (const free_of_non_heap &)base_other;
1426 return (same_tree_p (m_arg, other.m_arg)
1427 && m_freed_reg == other.m_freed_reg);
1430 int get_controlling_option () const final override
1432 return OPT_Wanalyzer_free_of_non_heap;
1435 bool emit (rich_location *rich_loc) final override
1437 auto_diagnostic_group d;
1438 diagnostic_metadata m;
1439 m.add_cwe (590); /* CWE-590: Free of Memory not on the Heap. */
1440 switch (get_memory_space ())
1442 default:
1443 case MEMSPACE_HEAP:
1444 gcc_unreachable ();
1445 case MEMSPACE_UNKNOWN:
1446 case MEMSPACE_CODE:
1447 case MEMSPACE_GLOBALS:
1448 case MEMSPACE_READONLY_DATA:
1449 return warning_meta (rich_loc, m, get_controlling_option (),
1450 "%<%s%> of %qE which points to memory"
1451 " not on the heap",
1452 m_funcname, m_arg);
1453 break;
1454 case MEMSPACE_STACK:
1455 return warning_meta (rich_loc, m, get_controlling_option (),
1456 "%<%s%> of %qE which points to memory"
1457 " on the stack",
1458 m_funcname, m_arg);
1459 break;
1463 label_text describe_state_change (const evdesc::state_change &)
1464 final override
1466 return label_text::borrow ("pointer is from here");
1469 label_text describe_final_event (const evdesc::final_event &ev) final override
1471 return ev.formatted_print ("call to %qs here", m_funcname);
1474 void mark_interesting_stuff (interesting_t *interest) final override
1476 if (m_freed_reg)
1477 interest->add_region_creation (m_freed_reg);
1480 private:
1481 enum memory_space get_memory_space () const
1483 if (m_freed_reg)
1484 return m_freed_reg->get_memory_space ();
1485 else
1486 return MEMSPACE_UNKNOWN;
1489 const region *m_freed_reg;
1490 const char *m_funcname;
1493 /* Concrete pending_diagnostic subclass for -Wanalyzer-deref-before-check. */
1495 class deref_before_check : public malloc_diagnostic
1497 public:
1498 deref_before_check (const malloc_state_machine &sm, tree arg)
1499 : malloc_diagnostic (sm, arg),
1500 m_deref_enode (NULL),
1501 m_check_enode (NULL)
1504 const char *get_kind () const final override { return "deref_before_check"; }
1506 int get_controlling_option () const final override
1508 return OPT_Wanalyzer_deref_before_check;
1511 bool emit (rich_location *rich_loc) final override
1513 /* Don't emit the warning if we can't show where the deref
1514 and the check occur. */
1515 if (!m_deref_enode)
1516 return false;
1517 if (!m_check_enode)
1518 return false;
1519 /* Only emit the warning for intraprocedural cases. */
1520 if (m_deref_enode->get_function () != m_check_enode->get_function ())
1521 return false;
1522 if (&m_deref_enode->get_point ().get_call_string ()
1523 != &m_check_enode->get_point ().get_call_string ())
1524 return false;
1526 /* Reject the warning if the check occurs within a macro defintion.
1527 This avoids false positives for such code as:
1529 #define throw_error \
1530 do { \
1531 if (p) \
1532 cleanup (p); \
1533 return; \
1534 } while (0)
1536 if (p->idx >= n)
1537 throw_error ();
1539 where the usage of "throw_error" implicitly adds a check
1540 on 'p'.
1542 We do warn when the check is in a macro expansion if we can get
1543 at the location of the condition and it is't part of the
1544 definition, so that we warn for checks such as:
1545 if (words[0][0] == '@')
1546 return;
1547 g_assert(words[0] != NULL); <--- here
1548 Unfortunately we don't have locations for individual gimple
1549 arguments, so in:
1550 g_assert (ptr);
1551 we merely have a gimple_cond
1552 if (p_2(D) == 0B)
1553 with no way of getting at the location of the condition separately
1554 from that of the gimple_cond (where the "if" is within the macro
1555 definition). We reject the warning for such cases.
1557 We do warn when the *deref* occurs in a macro, since this can be
1558 a source of real bugs; see e.g. PR 77425. */
1559 location_t check_loc = m_check_enode->get_point ().get_location ();
1560 if (linemap_location_from_macro_definition_p (line_table, check_loc))
1561 return false;
1563 /* Reject the warning if the deref's BB doesn't dominate that
1564 of the check, so that we don't warn e.g. for shared cleanup
1565 code that checks a pointer for NULL, when that code is sometimes
1566 used before a deref and sometimes after.
1567 Using the dominance code requires setting cfun. */
1568 auto_cfun sentinel (m_deref_enode->get_function ());
1569 calculate_dominance_info (CDI_DOMINATORS);
1570 if (!dominated_by_p (CDI_DOMINATORS,
1571 m_check_enode->get_supernode ()->m_bb,
1572 m_deref_enode->get_supernode ()->m_bb))
1573 return false;
1575 if (m_arg)
1576 return warning_at (rich_loc, get_controlling_option (),
1577 "check of %qE for NULL after already"
1578 " dereferencing it",
1579 m_arg);
1580 else
1581 return warning_at (rich_loc, get_controlling_option (),
1582 "check of pointer for NULL after already"
1583 " dereferencing it");
1586 label_text describe_state_change (const evdesc::state_change &change)
1587 final override
1589 if (change.m_old_state == m_sm.get_start_state ()
1590 && assumed_non_null_p (change.m_new_state))
1592 m_first_deref_event = change.m_event_id;
1593 m_deref_enode = change.m_event.get_exploded_node ();
1594 if (m_arg)
1595 return change.formatted_print ("pointer %qE is dereferenced here",
1596 m_arg);
1597 else
1598 return label_text::borrow ("pointer is dereferenced here");
1600 return malloc_diagnostic::describe_state_change (change);
1603 label_text describe_final_event (const evdesc::final_event &ev) final override
1605 m_check_enode = ev.m_event.get_exploded_node ();
1606 if (m_first_deref_event.known_p ())
1608 if (m_arg)
1609 return ev.formatted_print ("pointer %qE is checked for NULL here but"
1610 " it was already dereferenced at %@",
1611 m_arg, &m_first_deref_event);
1612 else
1613 return ev.formatted_print ("pointer is checked for NULL here but"
1614 " it was already dereferenced at %@",
1615 &m_first_deref_event);
1617 else
1619 if (m_arg)
1620 return ev.formatted_print ("pointer %qE is checked for NULL here but"
1621 " it was already dereferenced",
1622 m_arg);
1623 else
1624 return ev.formatted_print ("pointer is checked for NULL here but"
1625 " it was already dereferenced");
1629 private:
1630 diagnostic_event_id_t m_first_deref_event;
1631 const exploded_node *m_deref_enode;
1632 const exploded_node *m_check_enode;
1635 /* struct allocation_state : public state_machine::state. */
1637 /* Implementation of state_machine::state::dump_to_pp vfunc
1638 for allocation_state: append the API that this allocation is
1639 associated with. */
1641 void
1642 allocation_state::dump_to_pp (pretty_printer *pp) const
1644 state_machine::state::dump_to_pp (pp);
1645 if (m_deallocators)
1647 pp_string (pp, " (");
1648 m_deallocators->dump_to_pp (pp);
1649 pp_character (pp, ')');
1653 /* Given a allocation_state for a deallocator_set, get the "nonnull" state
1654 for the corresponding allocator(s). */
1656 const allocation_state *
1657 allocation_state::get_nonnull () const
1659 gcc_assert (m_deallocators);
1660 return as_a_allocation_state (m_deallocators->m_nonnull);
1663 /* struct assumed_non_null_state : public allocation_state. */
1665 void
1666 assumed_non_null_state::dump_to_pp (pretty_printer *pp) const
1668 allocation_state::dump_to_pp (pp);
1669 pp_string (pp, " (in ");
1670 m_frame->dump_to_pp (pp, true);
1671 pp_character (pp, ')');
1674 /* malloc_state_machine's ctor. */
1676 malloc_state_machine::malloc_state_machine (logger *logger)
1677 : state_machine ("malloc", logger),
1678 m_free (this, "free", WORDING_FREED),
1679 m_scalar_delete (this, "delete", WORDING_DELETED),
1680 m_vector_delete (this, "delete[]", WORDING_DELETED),
1681 m_realloc (this, "realloc", WORDING_REALLOCATED)
1683 gcc_assert (m_start->get_id () == 0);
1684 m_null = add_state ("null", RS_FREED, NULL, NULL);
1685 m_non_heap = add_state ("non-heap", RS_NON_HEAP, NULL, NULL);
1686 m_stop = add_state ("stop", RS_STOP, NULL, NULL);
1689 malloc_state_machine::~malloc_state_machine ()
1691 unsigned i;
1692 custom_deallocator_set *set;
1693 FOR_EACH_VEC_ELT (m_dynamic_sets, i, set)
1694 delete set;
1695 custom_deallocator *d;
1696 FOR_EACH_VEC_ELT (m_dynamic_deallocators, i, d)
1697 delete d;
1700 state_machine::state_t
1701 malloc_state_machine::add_state (const char *name, enum resource_state rs,
1702 const deallocator_set *deallocators,
1703 const deallocator *deallocator)
1705 return add_custom_state (new allocation_state (name, alloc_state_id (),
1706 rs, deallocators,
1707 deallocator));
1710 /* If ALLOCATOR_FNDECL has any "__attribute__((malloc(FOO)))",
1711 return a custom_deallocator_set for them, consolidating them
1712 to ensure uniqueness of the sets.
1714 Return NULL if it has no such attributes. */
1716 const custom_deallocator_set *
1717 malloc_state_machine::
1718 get_or_create_custom_deallocator_set (tree allocator_fndecl)
1720 /* Early rejection of decls without attributes. */
1721 tree attrs = DECL_ATTRIBUTES (allocator_fndecl);
1722 if (!attrs)
1723 return NULL;
1725 /* Otherwise, call maybe_create_custom_deallocator_set,
1726 memoizing the result. */
1727 if (custom_deallocator_set **slot
1728 = m_custom_deallocator_set_cache.get (allocator_fndecl))
1729 return *slot;
1730 custom_deallocator_set *set
1731 = maybe_create_custom_deallocator_set (allocator_fndecl);
1732 m_custom_deallocator_set_cache.put (allocator_fndecl, set);
1733 return set;
1736 /* Given ALLOCATOR_FNDECL, a FUNCTION_DECL with attributes,
1737 look for any "__attribute__((malloc(FOO)))" and return a
1738 custom_deallocator_set for them, consolidating them
1739 to ensure uniqueness of the sets.
1741 Return NULL if it has no such attributes.
1743 Subroutine of get_or_create_custom_deallocator_set which
1744 memoizes the result. */
1746 custom_deallocator_set *
1747 malloc_state_machine::
1748 maybe_create_custom_deallocator_set (tree allocator_fndecl)
1750 tree attrs = DECL_ATTRIBUTES (allocator_fndecl);
1751 gcc_assert (attrs);
1753 /* Look for instances of __attribute__((malloc(FOO))). */
1754 auto_vec<const deallocator *> deallocator_vec;
1755 for (tree allocs = attrs;
1756 (allocs = lookup_attribute ("malloc", allocs));
1757 allocs = TREE_CHAIN (allocs))
1759 tree args = TREE_VALUE (allocs);
1760 if (!args)
1761 continue;
1762 if (TREE_VALUE (args))
1764 const deallocator *d
1765 = get_or_create_deallocator (TREE_VALUE (args));
1766 deallocator_vec.safe_push (d);
1770 /* If there weren't any deallocators, bail. */
1771 if (deallocator_vec.length () == 0)
1772 return NULL;
1774 /* Consolidate, so that we reuse existing deallocator_set
1775 instances. */
1776 deallocator_vec.qsort (deallocator::cmp_ptr_ptr);
1777 custom_deallocator_set **slot
1778 = m_custom_deallocator_set_map.get (&deallocator_vec);
1779 if (slot)
1780 return *slot;
1781 custom_deallocator_set *set
1782 = new custom_deallocator_set (this, &deallocator_vec, WORDING_DEALLOCATED);
1783 m_custom_deallocator_set_map.put (&set->m_deallocator_vec, set);
1784 m_dynamic_sets.safe_push (set);
1785 return set;
1788 /* Get the deallocator for DEALLOCATOR_FNDECL, creating it if necessary. */
1790 const deallocator *
1791 malloc_state_machine::get_or_create_deallocator (tree deallocator_fndecl)
1793 deallocator **slot = m_deallocator_map.get (deallocator_fndecl);
1794 if (slot)
1795 return *slot;
1797 /* Reuse "free". */
1798 deallocator *d;
1799 if (is_named_call_p (deallocator_fndecl, "free")
1800 || is_std_named_call_p (deallocator_fndecl, "free")
1801 || is_named_call_p (deallocator_fndecl, "__builtin_free"))
1802 d = &m_free.m_deallocator;
1803 else
1805 custom_deallocator *cd
1806 = new custom_deallocator (this, deallocator_fndecl,
1807 WORDING_DEALLOCATED);
1808 m_dynamic_deallocators.safe_push (cd);
1809 d = cd;
1811 m_deallocator_map.put (deallocator_fndecl, d);
1812 return d;
1815 /* Get the "assumed-non-null" state for assumptions made within FRAME,
1816 creating it if necessary. */
1818 state_machine::state_t
1819 malloc_state_machine::
1820 get_or_create_assumed_non_null_state_for_frame (const frame_region *frame)
1822 if (state_t *slot = m_assumed_non_null.get (frame))
1823 return *slot;
1824 state_machine::state *new_state
1825 = new assumed_non_null_state ("assumed-non-null", alloc_state_id (), frame);
1826 add_custom_state (new_state);
1827 m_assumed_non_null.put (frame, new_state);
1828 return new_state;
1831 /* Try to identify the function declaration either by name or as a known malloc
1832 builtin. */
1834 static bool
1835 known_allocator_p (const_tree fndecl, const gcall *call)
1837 /* Either it is a function we know by name and number of arguments... */
1838 if (is_named_call_p (fndecl, "malloc", call, 1)
1839 || is_named_call_p (fndecl, "calloc", call, 2)
1840 || is_std_named_call_p (fndecl, "malloc", call, 1)
1841 || is_std_named_call_p (fndecl, "calloc", call, 2)
1842 || is_named_call_p (fndecl, "strdup", call, 1)
1843 || is_named_call_p (fndecl, "strndup", call, 2))
1844 return true;
1846 /* ... or it is a builtin allocator that allocates objects freed with
1847 __builtin_free. */
1848 if (fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
1849 switch (DECL_FUNCTION_CODE (fndecl))
1851 case BUILT_IN_MALLOC:
1852 case BUILT_IN_CALLOC:
1853 case BUILT_IN_STRDUP:
1854 case BUILT_IN_STRNDUP:
1855 return true;
1856 default:
1857 break;
1860 return false;
1863 /* If PTR's nullness is not known, transition it to the "assumed-non-null"
1864 state for the current frame. */
1866 void
1867 malloc_state_machine::maybe_assume_non_null (sm_context *sm_ctxt,
1868 tree ptr,
1869 const gimple *stmt) const
1871 const region_model *old_model = sm_ctxt->get_old_region_model ();
1872 if (!old_model)
1873 return;
1875 tree null_ptr_cst = build_int_cst (TREE_TYPE (ptr), 0);
1876 tristate known_non_null
1877 = old_model->eval_condition (ptr, NE_EXPR, null_ptr_cst, NULL);
1878 if (known_non_null.is_unknown ())
1880 /* Cast away const-ness for cache-like operations. */
1881 malloc_state_machine *mut_this
1882 = const_cast <malloc_state_machine *> (this);
1883 state_t next_state
1884 = mut_this->get_or_create_assumed_non_null_state_for_frame
1885 (old_model->get_current_frame ());
1886 sm_ctxt->set_next_state (stmt, ptr, next_state);
1890 /* Implementation of state_machine::on_stmt vfunc for malloc_state_machine. */
1892 bool
1893 malloc_state_machine::on_stmt (sm_context *sm_ctxt,
1894 const supernode *node,
1895 const gimple *stmt) const
1897 if (const gcall *call = dyn_cast <const gcall *> (stmt))
1898 if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call))
1900 if (known_allocator_p (callee_fndecl, call))
1902 on_allocator_call (sm_ctxt, call, &m_free);
1903 return true;
1906 if (is_named_call_p (callee_fndecl, "operator new", call, 1))
1907 on_allocator_call (sm_ctxt, call, &m_scalar_delete);
1908 else if (is_named_call_p (callee_fndecl, "operator new []", call, 1))
1909 on_allocator_call (sm_ctxt, call, &m_vector_delete);
1910 else if (is_named_call_p (callee_fndecl, "operator delete", call, 1)
1911 || is_named_call_p (callee_fndecl, "operator delete", call, 2))
1913 on_deallocator_call (sm_ctxt, node, call,
1914 &m_scalar_delete.m_deallocator, 0);
1915 return true;
1917 else if (is_named_call_p (callee_fndecl, "operator delete []", call, 1))
1919 on_deallocator_call (sm_ctxt, node, call,
1920 &m_vector_delete.m_deallocator, 0);
1921 return true;
1924 if (is_named_call_p (callee_fndecl, "alloca", call, 1)
1925 || is_named_call_p (callee_fndecl, "__builtin_alloca", call, 1))
1927 tree lhs = gimple_call_lhs (call);
1928 if (lhs)
1929 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_non_heap);
1930 return true;
1933 if (is_named_call_p (callee_fndecl, "free", call, 1)
1934 || is_std_named_call_p (callee_fndecl, "free", call, 1)
1935 || is_named_call_p (callee_fndecl, "__builtin_free", call, 1))
1937 on_deallocator_call (sm_ctxt, node, call,
1938 &m_free.m_deallocator, 0);
1939 return true;
1942 if (is_named_call_p (callee_fndecl, "realloc", call, 2)
1943 || is_named_call_p (callee_fndecl, "__builtin_realloc", call, 2))
1945 on_realloc_call (sm_ctxt, node, call);
1946 return true;
1949 if (unaffected_by_call_p (callee_fndecl))
1950 return true;
1952 /* Cast away const-ness for cache-like operations. */
1953 malloc_state_machine *mutable_this
1954 = const_cast <malloc_state_machine *> (this);
1956 /* Handle "__attribute__((malloc(FOO)))". */
1957 if (const deallocator_set *deallocators
1958 = mutable_this->get_or_create_custom_deallocator_set
1959 (callee_fndecl))
1961 tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (callee_fndecl));
1962 bool returns_nonnull
1963 = lookup_attribute ("returns_nonnull", attrs);
1964 on_allocator_call (sm_ctxt, call, deallocators, returns_nonnull);
1967 /* Handle "__attribute__((nonnull))". */
1969 tree fntype = TREE_TYPE (callee_fndecl);
1970 bitmap nonnull_args = get_nonnull_args (fntype);
1971 if (nonnull_args)
1973 for (unsigned i = 0; i < gimple_call_num_args (stmt); i++)
1975 tree arg = gimple_call_arg (stmt, i);
1976 if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
1977 continue;
1978 /* If we have a nonnull-args, and either all pointers, or just
1979 the specified pointers. */
1980 if (bitmap_empty_p (nonnull_args)
1981 || bitmap_bit_p (nonnull_args, i))
1983 state_t state = sm_ctxt->get_state (stmt, arg);
1984 /* Can't use a switch as the states are non-const. */
1985 if (unchecked_p (state))
1987 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1988 sm_ctxt->warn (node, stmt, arg,
1989 make_unique<possible_null_arg>
1990 (*this, diag_arg, callee_fndecl, i));
1991 const allocation_state *astate
1992 = as_a_allocation_state (state);
1993 sm_ctxt->set_next_state (stmt, arg,
1994 astate->get_nonnull ());
1996 else if (state == m_null)
1998 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1999 sm_ctxt->warn (node, stmt, arg,
2000 make_unique<null_arg>
2001 (*this, diag_arg, callee_fndecl, i));
2002 sm_ctxt->set_next_state (stmt, arg, m_stop);
2004 else if (state == m_start)
2005 maybe_assume_non_null (sm_ctxt, arg, stmt);
2008 BITMAP_FREE (nonnull_args);
2012 /* Check for this after nonnull, so that if we have both
2013 then we transition to "freed", rather than "checked". */
2014 unsigned dealloc_argno = fndecl_dealloc_argno (callee_fndecl);
2015 if (dealloc_argno != UINT_MAX)
2017 const deallocator *d
2018 = mutable_this->get_or_create_deallocator (callee_fndecl);
2019 on_deallocator_call (sm_ctxt, node, call, d, dealloc_argno);
2023 /* Look for pointers explicitly being compared against zero
2024 that are in state assumed_non_null i.e. we already defererenced
2025 them.
2026 We have to do this check here, rather than in on_condition
2027 because we add a constraint that the pointer is non-null when
2028 dereferencing it, and this makes the apply_constraints_for_gcond
2029 find known-true and known-false conditions; on_condition is only
2030 called when adding new constraints. */
2031 if (const gcond *cond_stmt = dyn_cast <const gcond *> (stmt))
2033 enum tree_code op = gimple_cond_code (cond_stmt);
2034 if (op == EQ_EXPR || op == NE_EXPR)
2036 tree lhs = gimple_cond_lhs (cond_stmt);
2037 tree rhs = gimple_cond_rhs (cond_stmt);
2038 if (any_pointer_p (lhs)
2039 && any_pointer_p (rhs)
2040 && zerop (rhs))
2042 state_t state = sm_ctxt->get_state (stmt, lhs);
2043 if (assumed_non_null_p (state))
2044 maybe_complain_about_deref_before_check
2045 (sm_ctxt, node,
2046 stmt,
2047 (const assumed_non_null_state *)state,
2048 lhs);
2053 if (tree lhs = sm_ctxt->is_zero_assignment (stmt))
2054 if (any_pointer_p (lhs))
2055 on_zero_assignment (sm_ctxt, stmt,lhs);
2057 /* Handle dereferences. */
2058 for (unsigned i = 0; i < gimple_num_ops (stmt); i++)
2060 tree op = gimple_op (stmt, i);
2061 if (!op)
2062 continue;
2063 if (TREE_CODE (op) == COMPONENT_REF)
2064 op = TREE_OPERAND (op, 0);
2066 if (TREE_CODE (op) == MEM_REF)
2068 tree arg = TREE_OPERAND (op, 0);
2070 state_t state = sm_ctxt->get_state (stmt, arg);
2071 if (state == m_start)
2072 maybe_assume_non_null (sm_ctxt, arg, stmt);
2073 else if (unchecked_p (state))
2075 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
2076 sm_ctxt->warn (node, stmt, arg,
2077 make_unique<possible_null_deref> (*this,
2078 diag_arg));
2079 const allocation_state *astate = as_a_allocation_state (state);
2080 sm_ctxt->set_next_state (stmt, arg, astate->get_nonnull ());
2082 else if (state == m_null)
2084 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
2085 sm_ctxt->warn (node, stmt, arg,
2086 make_unique<null_deref> (*this, diag_arg));
2087 sm_ctxt->set_next_state (stmt, arg, m_stop);
2089 else if (freed_p (state))
2091 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
2092 const allocation_state *astate = as_a_allocation_state (state);
2093 sm_ctxt->warn (node, stmt, arg,
2094 make_unique<use_after_free>
2095 (*this, diag_arg, astate->m_deallocator));
2096 sm_ctxt->set_next_state (stmt, arg, m_stop);
2100 return false;
2103 /* Given a check against null of PTR in assumed-non-null state STATE,
2104 potentially add a deref_before_check warning to SM_CTXT. */
2106 void
2107 malloc_state_machine::
2108 maybe_complain_about_deref_before_check (sm_context *sm_ctxt,
2109 const supernode *node,
2110 const gimple *stmt,
2111 const assumed_non_null_state *state,
2112 tree ptr) const
2114 const region_model *model = sm_ctxt->get_old_region_model ();
2115 if (!model)
2116 return;
2118 /* Don't complain if the current frame (where the check is occurring) is
2119 deeper than the frame in which the "not null" assumption was made.
2120 This suppress false positives for cases like:
2122 void foo (struct s *p)
2124 int val = s->some_field; // deref here
2125 shared_helper (p);
2128 where "shared_helper" has:
2130 void shared_helper (struct s *p)
2132 if (!p) // check here
2133 return;
2134 // etc
2137 since the check in "shared_helper" is OK. */
2138 const frame_region *checked_in_frame = model->get_current_frame ();
2139 const frame_region *assumed_nonnull_in_frame = state->m_frame;
2140 if (checked_in_frame->get_index () > assumed_nonnull_in_frame->get_index ())
2141 return;
2143 tree diag_ptr = sm_ctxt->get_diagnostic_tree (ptr);
2144 sm_ctxt->warn
2145 (node, stmt, ptr,
2146 make_unique<deref_before_check> (*this, diag_ptr));
2147 sm_ctxt->set_next_state (stmt, ptr, m_stop);
2150 /* Handle a call to an allocator.
2151 RETURNS_NONNULL is true if CALL is to a fndecl known to have
2152 __attribute__((returns_nonnull)). */
2154 void
2155 malloc_state_machine::on_allocator_call (sm_context *sm_ctxt,
2156 const gcall *call,
2157 const deallocator_set *deallocators,
2158 bool returns_nonnull) const
2160 tree lhs = gimple_call_lhs (call);
2161 if (lhs)
2163 if (sm_ctxt->get_state (call, lhs) == m_start)
2164 sm_ctxt->set_next_state (call, lhs,
2165 (returns_nonnull
2166 ? deallocators->m_nonnull
2167 : deallocators->m_unchecked));
2169 else
2171 /* TODO: report leak. */
2175 /* Handle deallocations of non-heap pointers.
2176 non-heap -> stop, with warning. */
2178 void
2179 malloc_state_machine::handle_free_of_non_heap (sm_context *sm_ctxt,
2180 const supernode *node,
2181 const gcall *call,
2182 tree arg,
2183 const deallocator *d) const
2185 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
2186 const region *freed_reg = NULL;
2187 if (const program_state *old_state = sm_ctxt->get_old_program_state ())
2189 const region_model *old_model = old_state->m_region_model;
2190 const svalue *ptr_sval = old_model->get_rvalue (arg, NULL);
2191 freed_reg = old_model->deref_rvalue (ptr_sval, arg, NULL);
2193 sm_ctxt->warn (node, call, arg,
2194 make_unique<free_of_non_heap>
2195 (*this, diag_arg, freed_reg, d->m_name));
2196 sm_ctxt->set_next_state (call, arg, m_stop);
2199 void
2200 malloc_state_machine::on_deallocator_call (sm_context *sm_ctxt,
2201 const supernode *node,
2202 const gcall *call,
2203 const deallocator *d,
2204 unsigned argno) const
2206 if (argno >= gimple_call_num_args (call))
2207 return;
2208 tree arg = gimple_call_arg (call, argno);
2210 state_t state = sm_ctxt->get_state (call, arg);
2212 /* start/assumed_non_null/unchecked/nonnull -> freed. */
2213 if (state == m_start || assumed_non_null_p (state))
2214 sm_ctxt->set_next_state (call, arg, d->m_freed);
2215 else if (unchecked_p (state) || nonnull_p (state))
2217 const allocation_state *astate = as_a_allocation_state (state);
2218 gcc_assert (astate->m_deallocators);
2219 if (!astate->m_deallocators->contains_p (d))
2221 /* Wrong allocator. */
2222 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
2223 sm_ctxt->warn (node, call, arg,
2224 make_unique<mismatching_deallocation>
2225 (*this, diag_arg,
2226 astate->m_deallocators,
2227 d));
2229 sm_ctxt->set_next_state (call, arg, d->m_freed);
2232 /* Keep state "null" as-is, rather than transitioning to "freed";
2233 we don't want to complain about double-free of NULL. */
2234 else if (state == d->m_freed)
2236 /* freed -> stop, with warning. */
2237 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
2238 sm_ctxt->warn (node, call, arg,
2239 make_unique<double_free> (*this, diag_arg, d->m_name));
2240 sm_ctxt->set_next_state (call, arg, m_stop);
2242 else if (state == m_non_heap)
2244 /* non-heap -> stop, with warning. */
2245 handle_free_of_non_heap (sm_ctxt, node, call, arg, d);
2249 /* Handle a call to "realloc".
2250 Check for free of non-heap or mismatching allocators,
2251 transitioning to the "stop" state for such cases.
2253 Otherwise, kf_realloc::impl_call_post will later
2254 get called (which will handle other sm-state transitions
2255 when the state is bifurcated). */
2257 void
2258 malloc_state_machine::on_realloc_call (sm_context *sm_ctxt,
2259 const supernode *node,
2260 const gcall *call) const
2262 const unsigned argno = 0;
2263 const deallocator *d = &m_realloc;
2265 tree arg = gimple_call_arg (call, argno);
2267 state_t state = sm_ctxt->get_state (call, arg);
2269 if (unchecked_p (state) || nonnull_p (state))
2271 const allocation_state *astate = as_a_allocation_state (state);
2272 gcc_assert (astate->m_deallocators);
2273 if (!astate->m_deallocators->contains_p (&m_free.m_deallocator))
2275 /* Wrong allocator. */
2276 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
2277 sm_ctxt->warn (node, call, arg,
2278 make_unique<mismatching_deallocation>
2279 (*this, diag_arg,
2280 astate->m_deallocators, d));
2281 sm_ctxt->set_next_state (call, arg, m_stop);
2282 if (path_context *path_ctxt = sm_ctxt->get_path_context ())
2283 path_ctxt->terminate_path ();
2286 else if (state == m_free.m_deallocator.m_freed)
2288 /* freed -> stop, with warning. */
2289 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
2290 sm_ctxt->warn (node, call, arg,
2291 make_unique<double_free> (*this, diag_arg, "free"));
2292 sm_ctxt->set_next_state (call, arg, m_stop);
2293 if (path_context *path_ctxt = sm_ctxt->get_path_context ())
2294 path_ctxt->terminate_path ();
2296 else if (state == m_non_heap)
2298 /* non-heap -> stop, with warning. */
2299 handle_free_of_non_heap (sm_ctxt, node, call, arg, d);
2300 if (path_context *path_ctxt = sm_ctxt->get_path_context ())
2301 path_ctxt->terminate_path ();
2305 /* Implementation of state_machine::on_phi vfunc for malloc_state_machine. */
2307 void
2308 malloc_state_machine::on_phi (sm_context *sm_ctxt,
2309 const supernode *node ATTRIBUTE_UNUSED,
2310 const gphi *phi,
2311 tree rhs) const
2313 if (zerop (rhs))
2315 tree lhs = gimple_phi_result (phi);
2316 on_zero_assignment (sm_ctxt, phi, lhs);
2320 /* Implementation of state_machine::on_condition vfunc for malloc_state_machine.
2321 Potentially transition state 'unchecked' to 'nonnull' or to 'null'. */
2323 void
2324 malloc_state_machine::on_condition (sm_context *sm_ctxt,
2325 const supernode *node ATTRIBUTE_UNUSED,
2326 const gimple *stmt,
2327 const svalue *lhs,
2328 enum tree_code op,
2329 const svalue *rhs) const
2331 if (!rhs->all_zeroes_p ())
2332 return;
2334 if (!any_pointer_p (lhs))
2335 return;
2336 if (!any_pointer_p (rhs))
2337 return;
2339 if (op == NE_EXPR)
2341 log ("got 'ARG != 0' match");
2342 state_t s = sm_ctxt->get_state (stmt, lhs);
2343 if (unchecked_p (s))
2345 const allocation_state *astate = as_a_allocation_state (s);
2346 sm_ctxt->set_next_state (stmt, lhs, astate->get_nonnull ());
2349 else if (op == EQ_EXPR)
2351 log ("got 'ARG == 0' match");
2352 state_t s = sm_ctxt->get_state (stmt, lhs);
2353 if (unchecked_p (s))
2354 sm_ctxt->set_next_state (stmt, lhs, m_null);
2358 /* Implementation of state_machine::on_pop_frame vfunc for malloc_state_machine.
2359 Clear any "assumed-non-null" state where the assumption happened in
2360 FRAME_REG. */
2362 void
2363 malloc_state_machine::on_pop_frame (sm_state_map *smap,
2364 const frame_region *frame_reg) const
2366 hash_set<const svalue *> svals_to_clear;
2367 for (auto kv : *smap)
2369 const svalue *sval = kv.first;
2370 state_t state = kv.second.m_state;
2371 if (assumed_non_null_p (state))
2373 const assumed_non_null_state *assumed_state
2374 = (const assumed_non_null_state *)state;
2375 if (frame_reg == assumed_state->m_frame)
2376 svals_to_clear.add (sval);
2379 for (auto sval : svals_to_clear)
2380 smap->clear_any_state (sval);
2383 /* Implementation of state_machine::can_purge_p vfunc for malloc_state_machine.
2384 Don't allow purging of pointers in state 'unchecked' or 'nonnull'
2385 (to avoid false leak reports). */
2387 bool
2388 malloc_state_machine::can_purge_p (state_t s) const
2390 enum resource_state rs = get_rs (s);
2391 return rs != RS_UNCHECKED && rs != RS_NONNULL;
2394 /* Implementation of state_machine::on_leak vfunc for malloc_state_machine
2395 (for complaining about leaks of pointers in state 'unchecked' and
2396 'nonnull'). */
2398 std::unique_ptr<pending_diagnostic>
2399 malloc_state_machine::on_leak (tree var) const
2401 return make_unique<malloc_leak> (*this, var);
2404 /* Implementation of state_machine::reset_when_passed_to_unknown_fn_p vfunc
2405 for malloc_state_machine. */
2407 bool
2408 malloc_state_machine::reset_when_passed_to_unknown_fn_p (state_t s,
2409 bool is_mutable) const
2411 /* An on-stack ptr doesn't stop being stack-allocated when passed to an
2412 unknown fn. */
2413 if (s == m_non_heap)
2414 return false;
2416 /* Otherwise, pointers passed as non-const can be freed. */
2417 return is_mutable;
2420 /* Implementation of state_machine::maybe_get_merged_states_nonequal vfunc
2421 for malloc_state_machine.
2423 Support discarding "assumed-non-null" states when merging with
2424 start state. */
2426 state_machine::state_t
2427 malloc_state_machine::maybe_get_merged_states_nonequal (state_t state_a,
2428 state_t state_b) const
2430 if (assumed_non_null_p (state_a) && state_b == m_start)
2431 return m_start;
2432 if (state_a == m_start && assumed_non_null_p (state_b))
2433 return m_start;
2434 return NULL;
2437 /* Return true if calls to FNDECL are known to not affect this sm-state. */
2439 bool
2440 malloc_state_machine::unaffected_by_call_p (tree fndecl)
2442 /* A set of functions that are known to not affect allocation
2443 status, even if we haven't fully modelled the rest of their
2444 behavior yet. */
2445 static const char * const funcnames[] = {
2446 /* This array must be kept sorted. */
2447 "strsep",
2449 const size_t count = ARRAY_SIZE (funcnames);
2450 function_set fs (funcnames, count);
2452 if (fs.contains_decl_p (fndecl))
2453 return true;
2455 return false;
2458 /* Shared logic for handling GIMPLE_ASSIGNs and GIMPLE_PHIs that
2459 assign zero to LHS. */
2461 void
2462 malloc_state_machine::on_zero_assignment (sm_context *sm_ctxt,
2463 const gimple *stmt,
2464 tree lhs) const
2466 state_t s = sm_ctxt->get_state (stmt, lhs);
2467 enum resource_state rs = get_rs (s);
2468 if (rs == RS_START
2469 || rs == RS_UNCHECKED
2470 || rs == RS_NONNULL
2471 || rs == RS_FREED)
2472 sm_ctxt->set_next_state (stmt, lhs, m_null);
2475 /* Special-case hook for handling realloc, for the "success with move to
2476 a new buffer" case, marking OLD_PTR_SVAL as freed and NEW_PTR_SVAL as
2477 non-null.
2479 This is similar to on_deallocator_call and on_allocator_call,
2480 but the checks happen in on_realloc_call, and by splitting the states. */
2482 void
2483 malloc_state_machine::
2484 on_realloc_with_move (region_model *model,
2485 sm_state_map *smap,
2486 const svalue *old_ptr_sval,
2487 const svalue *new_ptr_sval,
2488 const extrinsic_state &ext_state) const
2490 smap->set_state (model, old_ptr_sval,
2491 m_free.m_deallocator.m_freed,
2492 NULL, ext_state);
2494 smap->set_state (model, new_ptr_sval,
2495 m_free.m_nonnull,
2496 NULL, ext_state);
2499 } // anonymous namespace
2501 /* Internal interface to this file. */
2503 state_machine *
2504 make_malloc_state_machine (logger *logger)
2506 return new malloc_state_machine (logger);
2509 /* Specialcase hook for handling realloc, for use by
2510 kf_realloc::impl_call_post::success_with_move::update_model. */
2512 void
2513 region_model::on_realloc_with_move (const call_details &cd,
2514 const svalue *old_ptr_sval,
2515 const svalue *new_ptr_sval)
2517 region_model_context *ctxt = cd.get_ctxt ();
2518 if (!ctxt)
2519 return;
2520 const extrinsic_state *ext_state = ctxt->get_ext_state ();
2521 if (!ext_state)
2522 return;
2524 sm_state_map *smap;
2525 const state_machine *sm;
2526 unsigned sm_idx;
2527 if (!ctxt->get_malloc_map (&smap, &sm, &sm_idx))
2528 return;
2530 gcc_assert (smap);
2531 gcc_assert (sm);
2533 const malloc_state_machine &malloc_sm
2534 = (const malloc_state_machine &)*sm;
2536 malloc_sm.on_realloc_with_move (this,
2537 smap,
2538 old_ptr_sval,
2539 new_ptr_sval,
2540 *ext_state);
2543 } // namespace ana
2545 #endif /* #if ENABLE_ANALYZER */