]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/analyzer/sm-malloc.cc
ChangeLog: add problematic commit 2e96b5f14e4025691b57d2301d71aa6092ed44bc.
[thirdparty/gcc.git] / gcc / analyzer / sm-malloc.cc
CommitLineData
757bf1df 1/* A state machine for detecting misuses of the malloc/free API.
99dee823 2 Copyright (C) 2019-2021 Free Software Foundation, Inc.
757bf1df
DM
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it
8under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 3, or (at your option)
10any later version.
11
12GCC is distributed in the hope that it will be useful, but
13WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GCC; see the file COPYING3. If not see
19<http://www.gnu.org/licenses/>. */
20
21#include "config.h"
22#include "system.h"
23#include "coretypes.h"
24#include "tree.h"
25#include "function.h"
26#include "basic-block.h"
27#include "gimple.h"
28#include "options.h"
29#include "bitmap.h"
30#include "diagnostic-path.h"
31#include "diagnostic-metadata.h"
32#include "function.h"
809192e7 33#include "json.h"
757bf1df
DM
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"
808f4dfe
DM
39#include "tristate.h"
40#include "selftest.h"
41#include "analyzer/call-string.h"
42#include "analyzer/program-point.h"
43#include "analyzer/store.h"
44#include "analyzer/region-model.h"
c7e276b8
DM
45#include "stringpool.h"
46#include "attribs.h"
cd323d97 47#include "analyzer/function-set.h"
757bf1df
DM
48
49#if ENABLE_ANALYZER
50
75038aa6
DM
51namespace ana {
52
757bf1df
DM
53namespace {
54
c7e276b8
DM
55/* This state machine and its various support classes track allocations
56 and deallocations.
57
58 It has a few standard allocation/deallocation pairs (e.g. new/delete),
59 and also supports user-defined ones via
60 __attribute__ ((malloc(DEALLOCATOR))).
61
62 There can be more than one valid deallocator for a given allocator,
63 for example:
64 __attribute__ ((malloc (fclose)))
65 __attribute__ ((malloc (freopen, 3)))
66 FILE* fopen (const char*, const char*);
67 A deallocator_set represents a particular set of valid deallocators.
68
69 We track the expected deallocator_set for a value, but not the allocation
70 function - there could be more than one allocator per deallocator_set.
71 For example, there could be dozens of allocators for "free" beyond just
72 malloc e.g. calloc, xstrdup, etc. We don't want to explode the number
73 of states by tracking individual allocators in the exploded graph;
74 we merely want to track "this value expects to have 'free' called on it".
75 Perhaps we can reconstruct which allocator was used later, when emitting
76 the path, if it's necessary for precision of wording of diagnostics. */
77
78class deallocator;
79class deallocator_set;
1690a839
DM
80class malloc_state_machine;
81
82/* An enum for discriminating between different kinds of allocation_state. */
83
84enum resource_state
85{
c7e276b8 86 /* States that are independent of allocator/deallocator. */
1690a839
DM
87
88 /* The start state. */
89 RS_START,
90
91 /* State for a pointer that's known to be NULL. */
92 RS_NULL,
93
94 /* State for a pointer that's known to not be on the heap (e.g. to a local
95 or global). */
96 RS_NON_HEAP,
97
98 /* Stop state, for pointers we don't want to track any more. */
99 RS_STOP,
100
c7e276b8 101 /* States that relate to a specific deallocator_set. */
1690a839 102
c7e276b8 103 /* State for a pointer returned from an allocator that hasn't
1690a839
DM
104 been checked for NULL.
105 It could be a pointer to heap-allocated memory, or could be NULL. */
106 RS_UNCHECKED,
107
c7e276b8 108 /* State for a pointer returned from an allocator,
1690a839
DM
109 known to be non-NULL. */
110 RS_NONNULL,
111
c7e276b8 112 /* State for a pointer passed to a deallocator. */
1690a839
DM
113 RS_FREED
114};
115
c7e276b8
DM
116/* Custom state subclass, which can optionally refer to an a
117 deallocator_set. */
1690a839
DM
118
119struct allocation_state : public state_machine::state
120{
121 allocation_state (const char *name, unsigned id,
c7e276b8
DM
122 enum resource_state rs,
123 const deallocator_set *deallocators,
124 const deallocator *deallocator)
125 : state (name, id), m_rs (rs),
126 m_deallocators (deallocators),
127 m_deallocator (deallocator)
1690a839
DM
128 {}
129
130 void dump_to_pp (pretty_printer *pp) const FINAL OVERRIDE;
131
132 const allocation_state *get_nonnull () const;
133
134 enum resource_state m_rs;
c7e276b8
DM
135 const deallocator_set *m_deallocators;
136 const deallocator *m_deallocator;
1690a839
DM
137};
138
139/* An enum for choosing which wording to use in various diagnostics
140 when describing deallocations. */
141
142enum wording
143{
144 WORDING_FREED,
c7e276b8 145 WORDING_DELETED,
a6baafca
DM
146 WORDING_DEALLOCATED,
147 WORDING_REALLOCATED
1690a839
DM
148};
149
c7e276b8
DM
150/* Base class representing a deallocation function,
151 either a built-in one we know about, or one exposed via
152 __attribute__((malloc(DEALLOCATOR))). */
1690a839 153
c7e276b8 154struct deallocator
1690a839 155{
c7e276b8
DM
156 hashval_t hash () const;
157 void dump_to_pp (pretty_printer *pp) const;
158 static int cmp (const deallocator *a, const deallocator *b);
159 static int cmp_ptr_ptr (const void *, const void *);
1690a839 160
c7e276b8 161 /* Name to use in diagnostics. */
1690a839
DM
162 const char *m_name;
163
c7e276b8
DM
164 /* Which wording to use in diagnostics. */
165 enum wording m_wording;
166
167 /* State for a value passed to one of the deallocators. */
168 state_machine::state_t m_freed;
169
170protected:
171 deallocator (malloc_state_machine *sm,
172 const char *name,
173 enum wording wording);
174};
175
176/* Subclass representing a predefined deallocator.
177 e.g. "delete []", without needing a specific FUNCTION_DECL
178 ahead of time. */
179
180struct standard_deallocator : public deallocator
181{
182 standard_deallocator (malloc_state_machine *sm,
183 const char *name,
184 enum wording wording);
185};
186
187/* Subclass representing a user-defined deallocator
188 via __attribute__((malloc(DEALLOCATOR))) given
189 a specific FUNCTION_DECL. */
190
191struct custom_deallocator : public deallocator
192{
193 custom_deallocator (malloc_state_machine *sm,
194 tree deallocator_fndecl,
195 enum wording wording)
196 : deallocator (sm, IDENTIFIER_POINTER (DECL_NAME (deallocator_fndecl)),
197 wording)
198 {
199 }
200};
201
202/* Base class representing a set of possible deallocators.
203 Often this will be just a single deallocator, but some
204 allocators have multiple valid deallocators (e.g. the result of
205 "fopen" can be closed by either "fclose" or "freopen"). */
206
207struct deallocator_set
208{
209 deallocator_set (malloc_state_machine *sm,
210 enum wording wording);
211 virtual ~deallocator_set () {}
212
213 virtual bool contains_p (const deallocator *d) const = 0;
214 virtual const deallocator *maybe_get_single () const = 0;
215 virtual void dump_to_pp (pretty_printer *pp) const = 0;
216 void dump () const;
1690a839
DM
217
218 /* Which wording to use in diagnostics. */
219 enum wording m_wording;
220
c7e276b8 221 /* Pointers to states.
1690a839
DM
222 These states are owned by the state_machine base class. */
223
c7e276b8 224 /* State for an unchecked result from an allocator using this set. */
1690a839
DM
225 state_machine::state_t m_unchecked;
226
c7e276b8 227 /* State for a known non-NULL result from such an allocator. */
1690a839 228 state_machine::state_t m_nonnull;
c7e276b8 229};
1690a839 230
c7e276b8
DM
231/* Subclass of deallocator_set representing a set of deallocators
232 defined by one or more __attribute__((malloc(DEALLOCATOR))). */
233
234struct custom_deallocator_set : public deallocator_set
235{
236 typedef const auto_vec <const deallocator *> *key_t;
237
238 custom_deallocator_set (malloc_state_machine *sm,
239 const auto_vec <const deallocator *> *vec,
240 //const char *name,
241 //const char *dealloc_funcname,
242 //unsigned arg_idx,
243 enum wording wording);
244
245 bool contains_p (const deallocator *d) const FINAL OVERRIDE;
246 const deallocator *maybe_get_single () const FINAL OVERRIDE;
247 void dump_to_pp (pretty_printer *pp) const FINAL OVERRIDE;
248
249 auto_vec <const deallocator *> m_deallocator_vec;
250};
251
252/* Subclass of deallocator_set representing a set of deallocators
253 with a single standard_deallocator, e.g. "delete []". */
254
255struct standard_deallocator_set : public deallocator_set
256{
257 standard_deallocator_set (malloc_state_machine *sm,
258 const char *name,
259 enum wording wording);
260
261 bool contains_p (const deallocator *d) const FINAL OVERRIDE;
262 const deallocator *maybe_get_single () const FINAL OVERRIDE;
263 void dump_to_pp (pretty_printer *pp) const FINAL OVERRIDE;
264
265 standard_deallocator m_deallocator;
266};
267
268/* Traits class for ensuring uniqueness of deallocator_sets within
269 malloc_state_machine. */
270
271struct deallocator_set_map_traits
272{
273 typedef custom_deallocator_set::key_t key_type;
274 typedef custom_deallocator_set *value_type;
275 typedef custom_deallocator_set *compare_type;
276
277 static inline hashval_t hash (const key_type &k)
278 {
279 gcc_assert (k != NULL);
280 gcc_assert (k != reinterpret_cast<key_type> (1));
281
282 hashval_t result = 0;
283 unsigned i;
284 const deallocator *d;
285 FOR_EACH_VEC_ELT (*k, i, d)
286 result ^= d->hash ();
287 return result;
288 }
289 static inline bool equal_keys (const key_type &k1, const key_type &k2)
290 {
291 if (k1->length () != k2->length ())
292 return false;
293
294 for (unsigned i = 0; i < k1->length (); i++)
295 if ((*k1)[i] != (*k2)[i])
296 return false;
297
298 return true;
299 }
300 template <typename T>
301 static inline void remove (T &)
302 {
303 /* empty; the nodes are handled elsewhere. */
304 }
305 template <typename T>
306 static inline void mark_deleted (T &entry)
307 {
308 entry.m_key = reinterpret_cast<key_type> (1);
309 }
310 template <typename T>
311 static inline void mark_empty (T &entry)
312 {
313 entry.m_key = NULL;
314 }
315 template <typename T>
316 static inline bool is_deleted (const T &entry)
317 {
318 return entry.m_key == reinterpret_cast<key_type> (1);
319 }
320 template <typename T>
321 static inline bool is_empty (const T &entry)
322 {
323 return entry.m_key == NULL;
324 }
325 static const bool empty_zero_p = false;
1690a839
DM
326};
327
757bf1df
DM
328/* A state machine for detecting misuses of the malloc/free API.
329
330 See sm-malloc.dot for an overview (keep this in-sync with that file). */
331
332class malloc_state_machine : public state_machine
333{
334public:
1690a839
DM
335 typedef allocation_state custom_data_t;
336
757bf1df 337 malloc_state_machine (logger *logger);
c7e276b8 338 ~malloc_state_machine ();
757bf1df 339
1690a839 340 state_t
c7e276b8
DM
341 add_state (const char *name, enum resource_state rs,
342 const deallocator_set *deallocators,
343 const deallocator *deallocator);
1690a839 344
757bf1df
DM
345 bool inherited_state_p () const FINAL OVERRIDE { return false; }
346
808f4dfe
DM
347 state_machine::state_t
348 get_default_state (const svalue *sval) const FINAL OVERRIDE
349 {
350 if (tree cst = sval->maybe_get_constant ())
351 {
352 if (zerop (cst))
353 return m_null;
354 }
355 if (const region_svalue *ptr = sval->dyn_cast_region_svalue ())
356 {
357 const region *reg = ptr->get_pointee ();
15e7b93b
DM
358 const region *base_reg = reg->get_base_region ();
359 if (base_reg->get_kind () == RK_DECL
360 || base_reg->get_kind () == RK_STRING)
808f4dfe
DM
361 return m_non_heap;
362 }
363 return m_start;
364 }
365
757bf1df
DM
366 bool on_stmt (sm_context *sm_ctxt,
367 const supernode *node,
368 const gimple *stmt) const FINAL OVERRIDE;
369
8525d1f5
DM
370 void on_phi (sm_context *sm_ctxt,
371 const supernode *node,
372 const gphi *phi,
373 tree rhs) const FINAL OVERRIDE;
374
757bf1df
DM
375 void on_condition (sm_context *sm_ctxt,
376 const supernode *node,
377 const gimple *stmt,
48e8a7a6 378 const svalue *lhs,
757bf1df 379 enum tree_code op,
48e8a7a6 380 const svalue *rhs) const FINAL OVERRIDE;
757bf1df
DM
381
382 bool can_purge_p (state_t s) const FINAL OVERRIDE;
383 pending_diagnostic *on_leak (tree var) const FINAL OVERRIDE;
384
808f4dfe
DM
385 bool reset_when_passed_to_unknown_fn_p (state_t s,
386 bool is_mutable) const FINAL OVERRIDE;
387
cd323d97
DM
388 static bool unaffected_by_call_p (tree fndecl);
389
c7e276b8
DM
390 standard_deallocator_set m_free;
391 standard_deallocator_set m_scalar_delete;
392 standard_deallocator_set m_vector_delete;
1690a839 393
a6baafca
DM
394 standard_deallocator m_realloc;
395
1690a839 396 /* States that are independent of api. */
757bf1df
DM
397
398 /* State for a pointer that's known to be NULL. */
399 state_t m_null;
400
757bf1df
DM
401 /* State for a pointer that's known to not be on the heap (e.g. to a local
402 or global). */
403 state_t m_non_heap; // TODO: or should this be a different state machine?
404 // or do we need child values etc?
405
406 /* Stop state, for pointers we don't want to track any more. */
407 state_t m_stop;
8525d1f5
DM
408
409private:
c7e276b8
DM
410 const custom_deallocator_set *
411 get_or_create_custom_deallocator_set (tree allocator_fndecl);
412 custom_deallocator_set *
413 maybe_create_custom_deallocator_set (tree allocator_fndecl);
414 const deallocator *
415 get_or_create_deallocator (tree deallocator_fndecl);
416
1690a839
DM
417 void on_allocator_call (sm_context *sm_ctxt,
418 const gcall *call,
c7e276b8
DM
419 const deallocator_set *deallocators,
420 bool returns_nonnull = false) const;
1690a839
DM
421 void on_deallocator_call (sm_context *sm_ctxt,
422 const supernode *node,
423 const gcall *call,
c7e276b8
DM
424 const deallocator *d,
425 unsigned argno) const;
a6baafca
DM
426 void on_realloc_call (sm_context *sm_ctxt,
427 const supernode *node,
428 const gcall *call) const;
8525d1f5 429 void on_zero_assignment (sm_context *sm_ctxt,
8525d1f5
DM
430 const gimple *stmt,
431 tree lhs) const;
c7e276b8
DM
432
433 /* A map for consolidating deallocators so that they are
434 unique per deallocator FUNCTION_DECL. */
435 typedef hash_map<tree, deallocator *> deallocator_map_t;
436 deallocator_map_t m_deallocator_map;
437
438 /* Memoized lookups from FUNCTION_DECL to custom_deallocator_set *. */
439 typedef hash_map<tree, custom_deallocator_set *> deallocator_set_cache_t;
440 deallocator_set_cache_t m_custom_deallocator_set_cache;
441
442 /* A map for consolidating custom_deallocator_set instances. */
443 typedef hash_map<custom_deallocator_set::key_t,
444 custom_deallocator_set *,
445 deallocator_set_map_traits> custom_deallocator_set_map_t;
446 custom_deallocator_set_map_t m_custom_deallocator_set_map;
447
448 /* Record of dynamically-allocated objects, for cleanup. */
449 auto_vec <custom_deallocator_set *> m_dynamic_sets;
450 auto_vec <custom_deallocator *> m_dynamic_deallocators;
757bf1df
DM
451};
452
c7e276b8 453/* struct deallocator. */
1690a839 454
c7e276b8
DM
455deallocator::deallocator (malloc_state_machine *sm,
456 const char *name,
457 enum wording wording)
1690a839 458: m_name (name),
1690a839 459 m_wording (wording),
c7e276b8
DM
460 m_freed (sm->add_state ("freed", RS_FREED, NULL, this))
461{
462}
463
464hashval_t
465deallocator::hash () const
466{
467 return (hashval_t)m_freed->get_id ();
468}
469
470void
471deallocator::dump_to_pp (pretty_printer *pp) const
472{
473 pp_printf (pp, "%qs", m_name);
474}
475
476int
477deallocator::cmp (const deallocator *a, const deallocator *b)
478{
479 return (int)a->m_freed->get_id () - (int)b->m_freed->get_id ();
480}
481
482int
483deallocator::cmp_ptr_ptr (const void *a, const void *b)
484{
485 return cmp (*(const deallocator * const *)a,
486 *(const deallocator * const *)b);
487}
488
489
490/* struct standard_deallocator : public deallocator. */
491
492standard_deallocator::standard_deallocator (malloc_state_machine *sm,
493 const char *name,
494 enum wording wording)
495: deallocator (sm, name, wording)
1690a839
DM
496{
497}
498
c7e276b8
DM
499/* struct deallocator_set. */
500
501deallocator_set::deallocator_set (malloc_state_machine *sm,
502 enum wording wording)
503: m_wording (wording),
504 m_unchecked (sm->add_state ("unchecked", RS_UNCHECKED, this, NULL)),
505 m_nonnull (sm->add_state ("nonnull", RS_NONNULL, this, NULL))
506{
507}
508
509/* Dump a description of this deallocator_set to stderr. */
510
511DEBUG_FUNCTION void
512deallocator_set::dump () const
513{
514 pretty_printer pp;
515 pp_show_color (&pp) = pp_show_color (global_dc->printer);
516 pp.buffer->stream = stderr;
517 dump_to_pp (&pp);
518 pp_newline (&pp);
519 pp_flush (&pp);
520}
521
522/* struct custom_deallocator_set : public deallocator_set. */
523
524custom_deallocator_set::
525custom_deallocator_set (malloc_state_machine *sm,
526 const auto_vec <const deallocator *> *vec,
527 enum wording wording)
528: deallocator_set (sm, wording),
529 m_deallocator_vec (vec->length ())
530{
531 unsigned i;
532 const deallocator *d;
533 FOR_EACH_VEC_ELT (*vec, i, d)
534 m_deallocator_vec.safe_push (d);
535}
536
537bool
538custom_deallocator_set::contains_p (const deallocator *d) const
539{
540 unsigned i;
541 const deallocator *cd;
542 FOR_EACH_VEC_ELT (m_deallocator_vec, i, cd)
543 if (cd == d)
544 return true;
545 return false;
546}
547
548const deallocator *
549custom_deallocator_set::maybe_get_single () const
550{
551 if (m_deallocator_vec.length () == 1)
552 return m_deallocator_vec[0];
553 return NULL;
554}
555
556void
557custom_deallocator_set::dump_to_pp (pretty_printer *pp) const
558{
559 pp_character (pp, '{');
560 unsigned i;
561 const deallocator *d;
562 FOR_EACH_VEC_ELT (m_deallocator_vec, i, d)
563 {
564 if (i > 0)
565 pp_string (pp, ", ");
566 d->dump_to_pp (pp);
567 }
568 pp_character (pp, '}');
569}
570
571/* struct standard_deallocator_set : public deallocator_set. */
572
573standard_deallocator_set::standard_deallocator_set (malloc_state_machine *sm,
574 const char *name,
575 enum wording wording)
576: deallocator_set (sm, wording),
577 m_deallocator (sm, name, wording)
578{
579}
580
581bool
582standard_deallocator_set::contains_p (const deallocator *d) const
583{
584 return d == &m_deallocator;
585}
586
587const deallocator *
588standard_deallocator_set::maybe_get_single () const
589{
590 return &m_deallocator;
591}
592
593void
594standard_deallocator_set::dump_to_pp (pretty_printer *pp) const
595{
596 pp_character (pp, '{');
597 pp_string (pp, m_deallocator.m_name);
598 pp_character (pp, '}');
599}
600
1690a839
DM
601/* Return STATE cast to the custom state subclass, or NULL for the start state.
602 Everything should be an allocation_state apart from the start state. */
603
604static const allocation_state *
605dyn_cast_allocation_state (state_machine::state_t state)
606{
607 if (state->get_id () == 0)
608 return NULL;
609 return static_cast <const allocation_state *> (state);
610}
611
612/* Return STATE cast to the custom state subclass, for a state that is
613 already known to not be the start state . */
614
615static const allocation_state *
616as_a_allocation_state (state_machine::state_t state)
617{
618 gcc_assert (state->get_id () != 0);
619 return static_cast <const allocation_state *> (state);
620}
621
622/* Get the resource_state for STATE. */
623
624static enum resource_state
625get_rs (state_machine::state_t state)
626{
627 if (const allocation_state *astate = dyn_cast_allocation_state (state))
628 return astate->m_rs;
629 else
630 return RS_START;
631}
632
c7e276b8
DM
633/* Return true if STATE is the start state. */
634
635static bool
636start_p (state_machine::state_t state)
637{
638 return get_rs (state) == RS_START;
639}
640
1690a839
DM
641/* Return true if STATE is an unchecked result from an allocator. */
642
643static bool
644unchecked_p (state_machine::state_t state)
645{
646 return get_rs (state) == RS_UNCHECKED;
647}
648
649/* Return true if STATE is a non-null result from an allocator. */
650
651static bool
652nonnull_p (state_machine::state_t state)
653{
654 return get_rs (state) == RS_NONNULL;
655}
656
657/* Return true if STATE is a value that has been passed to a deallocator. */
658
659static bool
660freed_p (state_machine::state_t state)
661{
662 return get_rs (state) == RS_FREED;
663}
664
757bf1df
DM
665/* Class for diagnostics relating to malloc_state_machine. */
666
667class malloc_diagnostic : public pending_diagnostic
668{
669public:
670 malloc_diagnostic (const malloc_state_machine &sm, tree arg)
671 : m_sm (sm), m_arg (arg)
672 {}
673
674 bool subclass_equal_p (const pending_diagnostic &base_other) const OVERRIDE
675 {
14f9d7b9 676 return same_tree_p (m_arg, ((const malloc_diagnostic &)base_other).m_arg);
757bf1df
DM
677 }
678
679 label_text describe_state_change (const evdesc::state_change &change)
680 OVERRIDE
681 {
10fc42a8 682 if (change.m_old_state == m_sm.get_start_state ()
1690a839 683 && unchecked_p (change.m_new_state))
757bf1df
DM
684 // TODO: verify that it's the allocation stmt, not a copy
685 return label_text::borrow ("allocated here");
1690a839
DM
686 if (unchecked_p (change.m_old_state)
687 && nonnull_p (change.m_new_state))
808f4dfe
DM
688 {
689 if (change.m_expr)
690 return change.formatted_print ("assuming %qE is non-NULL",
691 change.m_expr);
692 else
693 return change.formatted_print ("assuming %qs is non-NULL",
694 "<unknown>");
695 }
757bf1df 696 if (change.m_new_state == m_sm.m_null)
0993ad65 697 {
1690a839 698 if (unchecked_p (change.m_old_state))
808f4dfe
DM
699 {
700 if (change.m_expr)
701 return change.formatted_print ("assuming %qE is NULL",
702 change.m_expr);
703 else
704 return change.formatted_print ("assuming %qs is NULL",
705 "<unknown>");
706 }
0993ad65 707 else
808f4dfe
DM
708 {
709 if (change.m_expr)
710 return change.formatted_print ("%qE is NULL",
711 change.m_expr);
712 else
713 return change.formatted_print ("%qs is NULL",
714 "<unknown>");
715 }
0993ad65
DM
716 }
717
757bf1df
DM
718 return label_text ();
719 }
720
721protected:
722 const malloc_state_machine &m_sm;
723 tree m_arg;
724};
725
1690a839
DM
726/* Concrete subclass for reporting mismatching allocator/deallocator
727 diagnostics. */
728
729class mismatching_deallocation : public malloc_diagnostic
730{
731public:
732 mismatching_deallocation (const malloc_state_machine &sm, tree arg,
c7e276b8
DM
733 const deallocator_set *expected_deallocators,
734 const deallocator *actual_dealloc)
1690a839 735 : malloc_diagnostic (sm, arg),
c7e276b8 736 m_expected_deallocators (expected_deallocators),
1690a839
DM
737 m_actual_dealloc (actual_dealloc)
738 {}
739
740 const char *get_kind () const FINAL OVERRIDE
741 {
742 return "mismatching_deallocation";
743 }
744
745 bool emit (rich_location *rich_loc) FINAL OVERRIDE
746 {
747 auto_diagnostic_group d;
748 diagnostic_metadata m;
749 m.add_cwe (762); /* CWE-762: Mismatched Memory Management Routines. */
c7e276b8
DM
750 if (const deallocator *expected_dealloc
751 = m_expected_deallocators->maybe_get_single ())
752 return warning_meta (rich_loc, m, OPT_Wanalyzer_mismatching_deallocation,
753 "%qE should have been deallocated with %qs"
754 " but was deallocated with %qs",
755 m_arg, expected_dealloc->m_name,
756 m_actual_dealloc->m_name);
757 else
758 return warning_meta (rich_loc, m, OPT_Wanalyzer_mismatching_deallocation,
759 "%qs called on %qE returned from a mismatched"
760 " allocation function",
761 m_actual_dealloc->m_name, m_arg);
1690a839
DM
762 }
763
764 label_text describe_state_change (const evdesc::state_change &change)
765 FINAL OVERRIDE
766 {
767 if (unchecked_p (change.m_new_state))
768 {
769 m_alloc_event = change.m_event_id;
c7e276b8
DM
770 if (const deallocator *expected_dealloc
771 = m_expected_deallocators->maybe_get_single ())
772 return change.formatted_print ("allocated here"
773 " (expects deallocation with %qs)",
774 expected_dealloc->m_name);
775 else
776 return change.formatted_print ("allocated here");
1690a839
DM
777 }
778 return malloc_diagnostic::describe_state_change (change);
779 }
780
781 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
782 {
783 if (m_alloc_event.known_p ())
c7e276b8
DM
784 {
785 if (const deallocator *expected_dealloc
786 = m_expected_deallocators->maybe_get_single ())
787 return ev.formatted_print
788 ("deallocated with %qs here;"
789 " allocation at %@ expects deallocation with %qs",
790 m_actual_dealloc->m_name, &m_alloc_event,
791 expected_dealloc->m_name);
792 else
793 return ev.formatted_print
794 ("deallocated with %qs here;"
795 " allocated at %@",
796 m_actual_dealloc->m_name, &m_alloc_event);
797 }
1690a839 798 return ev.formatted_print ("deallocated with %qs here",
c7e276b8 799 m_actual_dealloc->m_name);
1690a839
DM
800 }
801
802private:
803 diagnostic_event_id_t m_alloc_event;
c7e276b8
DM
804 const deallocator_set *m_expected_deallocators;
805 const deallocator *m_actual_dealloc;
1690a839
DM
806};
807
757bf1df
DM
808/* Concrete subclass for reporting double-free diagnostics. */
809
810class double_free : public malloc_diagnostic
811{
812public:
1690a839
DM
813 double_free (const malloc_state_machine &sm, tree arg, const char *funcname)
814 : malloc_diagnostic (sm, arg), m_funcname (funcname)
757bf1df
DM
815 {}
816
817 const char *get_kind () const FINAL OVERRIDE { return "double_free"; }
818
819 bool emit (rich_location *rich_loc) FINAL OVERRIDE
820 {
821 auto_diagnostic_group d;
822 diagnostic_metadata m;
823 m.add_cwe (415); /* CWE-415: Double Free. */
6c8e5844 824 return warning_meta (rich_loc, m, OPT_Wanalyzer_double_free,
c7e276b8 825 "double-%qs of %qE", m_funcname, m_arg);
757bf1df
DM
826 }
827
828 label_text describe_state_change (const evdesc::state_change &change)
829 FINAL OVERRIDE
830 {
1690a839 831 if (freed_p (change.m_new_state))
757bf1df
DM
832 {
833 m_first_free_event = change.m_event_id;
1690a839 834 return change.formatted_print ("first %qs here", m_funcname);
757bf1df
DM
835 }
836 return malloc_diagnostic::describe_state_change (change);
837 }
838
839 label_text describe_call_with_state (const evdesc::call_with_state &info)
840 FINAL OVERRIDE
841 {
1690a839 842 if (freed_p (info.m_state))
757bf1df
DM
843 return info.formatted_print
844 ("passing freed pointer %qE in call to %qE from %qE",
845 info.m_expr, info.m_callee_fndecl, info.m_caller_fndecl);
846 return label_text ();
847 }
848
849 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
850 {
851 if (m_first_free_event.known_p ())
852 return ev.formatted_print ("second %qs here; first %qs was at %@",
1690a839 853 m_funcname, m_funcname,
757bf1df 854 &m_first_free_event);
1690a839 855 return ev.formatted_print ("second %qs here", m_funcname);
757bf1df
DM
856 }
857
858private:
859 diagnostic_event_id_t m_first_free_event;
1690a839 860 const char *m_funcname;
757bf1df
DM
861};
862
863/* Abstract subclass for describing possible bad uses of NULL.
864 Responsible for describing the call that could return NULL. */
865
866class possible_null : public malloc_diagnostic
867{
868public:
869 possible_null (const malloc_state_machine &sm, tree arg)
870 : malloc_diagnostic (sm, arg)
871 {}
872
873 label_text describe_state_change (const evdesc::state_change &change)
874 FINAL OVERRIDE
875 {
10fc42a8 876 if (change.m_old_state == m_sm.get_start_state ()
1690a839 877 && unchecked_p (change.m_new_state))
757bf1df
DM
878 {
879 m_origin_of_unchecked_event = change.m_event_id;
880 return label_text::borrow ("this call could return NULL");
881 }
882 return malloc_diagnostic::describe_state_change (change);
883 }
884
885 label_text describe_return_of_state (const evdesc::return_of_state &info)
886 FINAL OVERRIDE
887 {
1690a839 888 if (unchecked_p (info.m_state))
757bf1df
DM
889 return info.formatted_print ("possible return of NULL to %qE from %qE",
890 info.m_caller_fndecl, info.m_callee_fndecl);
891 return label_text ();
892 }
893
894protected:
895 diagnostic_event_id_t m_origin_of_unchecked_event;
896};
897
898/* Concrete subclass for describing dereference of a possible NULL
899 value. */
900
901class possible_null_deref : public possible_null
902{
903public:
904 possible_null_deref (const malloc_state_machine &sm, tree arg)
905 : possible_null (sm, arg)
906 {}
907
908 const char *get_kind () const FINAL OVERRIDE { return "possible_null_deref"; }
909
910 bool emit (rich_location *rich_loc) FINAL OVERRIDE
911 {
912 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
913 diagnostic_metadata m;
914 m.add_cwe (690);
6c8e5844
DM
915 return warning_meta (rich_loc, m,
916 OPT_Wanalyzer_possible_null_dereference,
917 "dereference of possibly-NULL %qE", m_arg);
757bf1df
DM
918 }
919
920 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
921 {
922 if (m_origin_of_unchecked_event.known_p ())
923 return ev.formatted_print ("%qE could be NULL: unchecked value from %@",
924 ev.m_expr,
925 &m_origin_of_unchecked_event);
926 else
927 return ev.formatted_print ("%qE could be NULL", ev.m_expr);
928 }
929
930};
931
2f7c50b7
DM
932/* Return true if FNDECL is a C++ method. */
933
934static bool
935method_p (tree fndecl)
936{
937 return TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE;
938}
939
940/* Return a 1-based description of ARG_IDX (0-based) of FNDECL.
941 Compare with %P in the C++ FE (implemented in cp/error.c: parm_to_string
942 as called from cp_printer). */
943
944static label_text
945describe_argument_index (tree fndecl, int arg_idx)
946{
947 if (method_p (fndecl))
948 if (arg_idx == 0)
949 return label_text::borrow ("'this'");
950 pretty_printer pp;
951 pp_printf (&pp, "%u", arg_idx + 1 - method_p (fndecl));
952 return label_text::take (xstrdup (pp_formatted_text (&pp)));
953}
954
757bf1df
DM
955/* Subroutine for use by possible_null_arg::emit and null_arg::emit.
956 Issue a note informing that the pertinent argument must be non-NULL. */
957
958static void
959inform_nonnull_attribute (tree fndecl, int arg_idx)
960{
2f7c50b7 961 label_text arg_desc = describe_argument_index (fndecl, arg_idx);
757bf1df 962 inform (DECL_SOURCE_LOCATION (fndecl),
2f7c50b7
DM
963 "argument %s of %qD must be non-null",
964 arg_desc.m_buffer, fndecl);
965 arg_desc.maybe_free ();
757bf1df
DM
966 /* Ideally we would use the location of the parm and underline the
967 attribute also - but we don't have the location_t values at this point
968 in the middle-end.
969 For reference, the C and C++ FEs have get_fndecl_argument_location. */
970}
971
972/* Concrete subclass for describing passing a possibly-NULL value to a
973 function marked with __attribute__((nonnull)). */
974
975class possible_null_arg : public possible_null
976{
977public:
978 possible_null_arg (const malloc_state_machine &sm, tree arg,
979 tree fndecl, int arg_idx)
980 : possible_null (sm, arg),
981 m_fndecl (fndecl), m_arg_idx (arg_idx)
982 {}
983
984 const char *get_kind () const FINAL OVERRIDE { return "possible_null_arg"; }
985
986 bool subclass_equal_p (const pending_diagnostic &base_other) const
987 {
988 const possible_null_arg &sub_other
989 = (const possible_null_arg &)base_other;
14f9d7b9 990 return (same_tree_p (m_arg, sub_other.m_arg)
757bf1df
DM
991 && m_fndecl == sub_other.m_fndecl
992 && m_arg_idx == sub_other.m_arg_idx);
993 }
994
995
996 bool emit (rich_location *rich_loc) FINAL OVERRIDE
997 {
998 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
999 auto_diagnostic_group d;
1000 diagnostic_metadata m;
1001 m.add_cwe (690);
1002 bool warned
6c8e5844
DM
1003 = warning_meta (rich_loc, m, OPT_Wanalyzer_possible_null_argument,
1004 "use of possibly-NULL %qE where non-null expected",
1005 m_arg);
757bf1df
DM
1006 if (warned)
1007 inform_nonnull_attribute (m_fndecl, m_arg_idx);
1008 return warned;
1009 }
1010
1011 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
1012 {
2f7c50b7
DM
1013 label_text arg_desc = describe_argument_index (m_fndecl, m_arg_idx);
1014 label_text result;
757bf1df 1015 if (m_origin_of_unchecked_event.known_p ())
2f7c50b7
DM
1016 result = ev.formatted_print ("argument %s (%qE) from %@ could be NULL"
1017 " where non-null expected",
1018 arg_desc.m_buffer, ev.m_expr,
1019 &m_origin_of_unchecked_event);
757bf1df 1020 else
2f7c50b7
DM
1021 result = ev.formatted_print ("argument %s (%qE) could be NULL"
1022 " where non-null expected",
1023 arg_desc.m_buffer, ev.m_expr);
1024 arg_desc.maybe_free ();
1025 return result;
757bf1df
DM
1026 }
1027
1028private:
1029 tree m_fndecl;
1030 int m_arg_idx;
1031};
1032
1033/* Concrete subclass for describing a dereference of a NULL value. */
1034
1035class null_deref : public malloc_diagnostic
1036{
1037public:
1038 null_deref (const malloc_state_machine &sm, tree arg)
1039 : malloc_diagnostic (sm, arg) {}
1040
1041 const char *get_kind () const FINAL OVERRIDE { return "null_deref"; }
1042
1043 bool emit (rich_location *rich_loc) FINAL OVERRIDE
1044 {
f3f312b5 1045 /* CWE-476: NULL Pointer Dereference. */
757bf1df 1046 diagnostic_metadata m;
f3f312b5 1047 m.add_cwe (476);
6c8e5844
DM
1048 return warning_meta (rich_loc, m,
1049 OPT_Wanalyzer_null_dereference,
1050 "dereference of NULL %qE", m_arg);
757bf1df
DM
1051 }
1052
1053 label_text describe_return_of_state (const evdesc::return_of_state &info)
1054 FINAL OVERRIDE
1055 {
1056 if (info.m_state == m_sm.m_null)
1057 return info.formatted_print ("return of NULL to %qE from %qE",
1058 info.m_caller_fndecl, info.m_callee_fndecl);
1059 return label_text ();
1060 }
1061
1062 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
1063 {
1064 return ev.formatted_print ("dereference of NULL %qE", ev.m_expr);
1065 }
1066};
1067
1068/* Concrete subclass for describing passing a NULL value to a
1069 function marked with __attribute__((nonnull)). */
1070
1071class null_arg : public malloc_diagnostic
1072{
1073public:
1074 null_arg (const malloc_state_machine &sm, tree arg,
1075 tree fndecl, int arg_idx)
1076 : malloc_diagnostic (sm, arg),
1077 m_fndecl (fndecl), m_arg_idx (arg_idx)
1078 {}
1079
1080 const char *get_kind () const FINAL OVERRIDE { return "null_arg"; }
1081
1082 bool subclass_equal_p (const pending_diagnostic &base_other) const
1083 {
1084 const null_arg &sub_other
1085 = (const null_arg &)base_other;
14f9d7b9 1086 return (same_tree_p (m_arg, sub_other.m_arg)
757bf1df
DM
1087 && m_fndecl == sub_other.m_fndecl
1088 && m_arg_idx == sub_other.m_arg_idx);
1089 }
1090
1091 bool emit (rich_location *rich_loc) FINAL OVERRIDE
1092 {
f3f312b5 1093 /* CWE-476: NULL Pointer Dereference. */
757bf1df
DM
1094 auto_diagnostic_group d;
1095 diagnostic_metadata m;
f3f312b5 1096 m.add_cwe (476);
808f4dfe
DM
1097
1098 bool warned;
1099 if (zerop (m_arg))
1100 warned = warning_meta (rich_loc, m, OPT_Wanalyzer_null_argument,
1101 "use of NULL where non-null expected");
1102 else
1103 warned = warning_meta (rich_loc, m, OPT_Wanalyzer_null_argument,
1104 "use of NULL %qE where non-null expected",
1105 m_arg);
757bf1df
DM
1106 if (warned)
1107 inform_nonnull_attribute (m_fndecl, m_arg_idx);
1108 return warned;
1109 }
1110
1111 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
1112 {
2f7c50b7
DM
1113 label_text arg_desc = describe_argument_index (m_fndecl, m_arg_idx);
1114 label_text result;
808f4dfe 1115 if (zerop (ev.m_expr))
2f7c50b7
DM
1116 result = ev.formatted_print ("argument %s NULL where non-null expected",
1117 arg_desc.m_buffer);
808f4dfe 1118 else
2f7c50b7
DM
1119 result = ev.formatted_print ("argument %s (%qE) NULL"
1120 " where non-null expected",
1121 arg_desc.m_buffer, ev.m_expr);
1122 arg_desc.maybe_free ();
1123 return result;
757bf1df
DM
1124 }
1125
1126private:
1127 tree m_fndecl;
1128 int m_arg_idx;
1129};
1130
1131class use_after_free : public malloc_diagnostic
1132{
1133public:
1690a839 1134 use_after_free (const malloc_state_machine &sm, tree arg,
c7e276b8
DM
1135 const deallocator *deallocator)
1136 : malloc_diagnostic (sm, arg),
1137 m_deallocator (deallocator)
1138 {
1139 gcc_assert (deallocator);
1140 }
757bf1df
DM
1141
1142 const char *get_kind () const FINAL OVERRIDE { return "use_after_free"; }
1143
1144 bool emit (rich_location *rich_loc) FINAL OVERRIDE
1145 {
1146 /* CWE-416: Use After Free. */
1147 diagnostic_metadata m;
1148 m.add_cwe (416);
6c8e5844 1149 return warning_meta (rich_loc, m, OPT_Wanalyzer_use_after_free,
1690a839 1150 "use after %<%s%> of %qE",
c7e276b8 1151 m_deallocator->m_name, m_arg);
757bf1df
DM
1152 }
1153
1154 label_text describe_state_change (const evdesc::state_change &change)
1155 FINAL OVERRIDE
1156 {
1690a839 1157 if (freed_p (change.m_new_state))
757bf1df
DM
1158 {
1159 m_free_event = change.m_event_id;
c7e276b8 1160 switch (m_deallocator->m_wording)
1690a839
DM
1161 {
1162 default:
a6baafca 1163 case WORDING_REALLOCATED:
1690a839
DM
1164 gcc_unreachable ();
1165 case WORDING_FREED:
1166 return label_text::borrow ("freed here");
1167 case WORDING_DELETED:
1168 return label_text::borrow ("deleted here");
c7e276b8
DM
1169 case WORDING_DEALLOCATED:
1170 return label_text::borrow ("deallocated here");
1690a839 1171 }
757bf1df
DM
1172 }
1173 return malloc_diagnostic::describe_state_change (change);
1174 }
1175
1176 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
1177 {
c7e276b8 1178 const char *funcname = m_deallocator->m_name;
757bf1df 1179 if (m_free_event.known_p ())
c7e276b8 1180 switch (m_deallocator->m_wording)
1690a839
DM
1181 {
1182 default:
a6baafca 1183 case WORDING_REALLOCATED:
1690a839
DM
1184 gcc_unreachable ();
1185 case WORDING_FREED:
1186 return ev.formatted_print ("use after %<%s%> of %qE; freed at %@",
1187 funcname, ev.m_expr, &m_free_event);
1188 case WORDING_DELETED:
1189 return ev.formatted_print ("use after %<%s%> of %qE; deleted at %@",
1190 funcname, ev.m_expr, &m_free_event);
c7e276b8
DM
1191 case WORDING_DEALLOCATED:
1192 return ev.formatted_print ("use after %<%s%> of %qE;"
1193 " deallocated at %@",
1194 funcname, ev.m_expr, &m_free_event);
1690a839 1195 }
757bf1df 1196 else
1690a839
DM
1197 return ev.formatted_print ("use after %<%s%> of %qE",
1198 funcname, ev.m_expr);
757bf1df
DM
1199 }
1200
33255ad3
DM
1201 /* Implementation of pending_diagnostic::supercedes_p for
1202 use_after_free.
1203
1204 We want use-after-free to supercede use-of-unitialized-value,
1205 so that if we have these at the same stmt, we don't emit
1206 a use-of-uninitialized, just the use-after-free.
1207 (this is because we fully purge information about freed
1208 buffers when we free them to avoid state explosions, so
1209 that if they are accessed after the free, it looks like
1210 they are uninitialized). */
1211
1212 bool supercedes_p (const pending_diagnostic &other) const FINAL OVERRIDE
1213 {
1214 if (other.use_of_uninit_p ())
1215 return true;
1216
1217 return false;
1218 }
1219
757bf1df
DM
1220private:
1221 diagnostic_event_id_t m_free_event;
c7e276b8 1222 const deallocator *m_deallocator;
757bf1df
DM
1223};
1224
1225class malloc_leak : public malloc_diagnostic
1226{
1227public:
1228 malloc_leak (const malloc_state_machine &sm, tree arg)
1229 : malloc_diagnostic (sm, arg) {}
1230
1231 const char *get_kind () const FINAL OVERRIDE { return "malloc_leak"; }
1232
1233 bool emit (rich_location *rich_loc) FINAL OVERRIDE
1234 {
1235 diagnostic_metadata m;
1236 m.add_cwe (401);
808f4dfe
DM
1237 if (m_arg)
1238 return warning_meta (rich_loc, m, OPT_Wanalyzer_malloc_leak,
1239 "leak of %qE", m_arg);
1240 else
1241 return warning_meta (rich_loc, m, OPT_Wanalyzer_malloc_leak,
1242 "leak of %qs", "<unknown>");
757bf1df
DM
1243 }
1244
1245 label_text describe_state_change (const evdesc::state_change &change)
1246 FINAL OVERRIDE
1247 {
c7e276b8
DM
1248 if (unchecked_p (change.m_new_state)
1249 || (start_p (change.m_old_state) && nonnull_p (change.m_new_state)))
757bf1df 1250 {
1690a839 1251 m_alloc_event = change.m_event_id;
757bf1df
DM
1252 return label_text::borrow ("allocated here");
1253 }
1254 return malloc_diagnostic::describe_state_change (change);
1255 }
1256
1257 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
1258 {
808f4dfe
DM
1259 if (ev.m_expr)
1260 {
1690a839 1261 if (m_alloc_event.known_p ())
808f4dfe 1262 return ev.formatted_print ("%qE leaks here; was allocated at %@",
1690a839 1263 ev.m_expr, &m_alloc_event);
808f4dfe
DM
1264 else
1265 return ev.formatted_print ("%qE leaks here", ev.m_expr);
1266 }
757bf1df 1267 else
808f4dfe 1268 {
1690a839 1269 if (m_alloc_event.known_p ())
808f4dfe 1270 return ev.formatted_print ("%qs leaks here; was allocated at %@",
1690a839 1271 "<unknown>", &m_alloc_event);
808f4dfe
DM
1272 else
1273 return ev.formatted_print ("%qs leaks here", "<unknown>");
1274 }
757bf1df
DM
1275 }
1276
1277private:
1690a839 1278 diagnostic_event_id_t m_alloc_event;
757bf1df
DM
1279};
1280
1281class free_of_non_heap : public malloc_diagnostic
1282{
1283public:
1690a839
DM
1284 free_of_non_heap (const malloc_state_machine &sm, tree arg,
1285 const char *funcname)
1286 : malloc_diagnostic (sm, arg), m_funcname (funcname), m_kind (KIND_UNKNOWN)
757bf1df
DM
1287 {
1288 }
1289
1290 const char *get_kind () const FINAL OVERRIDE { return "free_of_non_heap"; }
1291
1292 bool subclass_equal_p (const pending_diagnostic &base_other) const
1293 FINAL OVERRIDE
1294 {
1295 const free_of_non_heap &other = (const free_of_non_heap &)base_other;
14f9d7b9 1296 return (same_tree_p (m_arg, other.m_arg) && m_kind == other.m_kind);
757bf1df
DM
1297 }
1298
1299 bool emit (rich_location *rich_loc) FINAL OVERRIDE
1300 {
1301 auto_diagnostic_group d;
1302 diagnostic_metadata m;
1303 m.add_cwe (590); /* CWE-590: Free of Memory not on the Heap. */
1304 switch (m_kind)
1305 {
1306 default:
1307 gcc_unreachable ();
1308 case KIND_UNKNOWN:
6c8e5844 1309 return warning_meta (rich_loc, m, OPT_Wanalyzer_free_of_non_heap,
1690a839 1310 "%<%s%> of %qE which points to memory"
6c8e5844 1311 " not on the heap",
1690a839 1312 m_funcname, m_arg);
757bf1df
DM
1313 break;
1314 case KIND_ALLOCA:
6c8e5844 1315 return warning_meta (rich_loc, m, OPT_Wanalyzer_free_of_non_heap,
1690a839 1316 "%<%s%> of memory allocated on the stack by"
6c8e5844 1317 " %qs (%qE) will corrupt the heap",
1690a839 1318 m_funcname, "alloca", m_arg);
757bf1df
DM
1319 break;
1320 }
1321 }
1322
1323 label_text describe_state_change (const evdesc::state_change &change)
1324 FINAL OVERRIDE
1325 {
1326 /* Attempt to reconstruct what kind of pointer it is.
1327 (It seems neater for this to be a part of the state, though). */
61bfff56 1328 if (change.m_expr && TREE_CODE (change.m_expr) == SSA_NAME)
757bf1df
DM
1329 {
1330 gimple *def_stmt = SSA_NAME_DEF_STMT (change.m_expr);
1331 if (gcall *call = dyn_cast <gcall *> (def_stmt))
1332 {
1333 if (is_special_named_call_p (call, "alloca", 1)
1334 || is_special_named_call_p (call, "__builtin_alloca", 1))
1335 {
1336 m_kind = KIND_ALLOCA;
1337 return label_text::borrow
1338 ("memory is allocated on the stack here");
1339 }
1340 }
1341 }
1342 return label_text::borrow ("pointer is from here");
1343 }
1344
1345 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
1346 {
1690a839 1347 return ev.formatted_print ("call to %qs here", m_funcname);
757bf1df
DM
1348 }
1349
1350private:
1351 enum kind
1352 {
1353 KIND_UNKNOWN,
1354 KIND_ALLOCA
1355 };
1690a839 1356 const char *m_funcname;
757bf1df
DM
1357 enum kind m_kind;
1358};
1359
1690a839
DM
1360/* struct allocation_state : public state_machine::state. */
1361
1362/* Implementation of state_machine::state::dump_to_pp vfunc
1363 for allocation_state: append the API that this allocation is
1364 associated with. */
1365
1366void
1367allocation_state::dump_to_pp (pretty_printer *pp) const
1368{
1369 state_machine::state::dump_to_pp (pp);
c7e276b8
DM
1370 if (m_deallocators)
1371 {
1372 pp_string (pp, " (");
1373 m_deallocators->dump_to_pp (pp);
1374 pp_character (pp, ')');
1375 }
1690a839
DM
1376}
1377
c7e276b8
DM
1378/* Given a allocation_state for a deallocator_set, get the "nonnull" state
1379 for the corresponding allocator(s). */
1690a839
DM
1380
1381const allocation_state *
1382allocation_state::get_nonnull () const
1383{
c7e276b8
DM
1384 gcc_assert (m_deallocators);
1385 return as_a_allocation_state (m_deallocators->m_nonnull);
1690a839
DM
1386}
1387
757bf1df
DM
1388/* malloc_state_machine's ctor. */
1389
1390malloc_state_machine::malloc_state_machine (logger *logger)
1690a839 1391: state_machine ("malloc", logger),
c7e276b8
DM
1392 m_free (this, "free", WORDING_FREED),
1393 m_scalar_delete (this, "delete", WORDING_DELETED),
a6baafca
DM
1394 m_vector_delete (this, "delete[]", WORDING_DELETED),
1395 m_realloc (this, "realloc", WORDING_REALLOCATED)
1690a839
DM
1396{
1397 gcc_assert (m_start->get_id () == 0);
c7e276b8
DM
1398 m_null = add_state ("null", RS_FREED, NULL, NULL);
1399 m_non_heap = add_state ("non-heap", RS_NON_HEAP, NULL, NULL);
1400 m_stop = add_state ("stop", RS_STOP, NULL, NULL);
1401}
1402
1403malloc_state_machine::~malloc_state_machine ()
1404{
1405 unsigned i;
1406 custom_deallocator_set *set;
1407 FOR_EACH_VEC_ELT (m_dynamic_sets, i, set)
1408 delete set;
1409 custom_deallocator *d;
1410 FOR_EACH_VEC_ELT (m_dynamic_deallocators, i, d)
1411 delete d;
1690a839
DM
1412}
1413
1414state_machine::state_t
1415malloc_state_machine::add_state (const char *name, enum resource_state rs,
c7e276b8
DM
1416 const deallocator_set *deallocators,
1417 const deallocator *deallocator)
1690a839
DM
1418{
1419 return add_custom_state (new allocation_state (name, alloc_state_id (),
c7e276b8
DM
1420 rs, deallocators,
1421 deallocator));
1422}
1423
1424/* If ALLOCATOR_FNDECL has any "__attribute__((malloc(FOO)))",
1425 return a custom_deallocator_set for them, consolidating them
1426 to ensure uniqueness of the sets.
1427
1428 Return NULL if it has no such attributes. */
1429
1430const custom_deallocator_set *
1431malloc_state_machine::
1432get_or_create_custom_deallocator_set (tree allocator_fndecl)
1433{
1434 /* Early rejection of decls without attributes. */
1435 tree attrs = DECL_ATTRIBUTES (allocator_fndecl);
1436 if (!attrs)
1437 return NULL;
1438
1439 /* Otherwise, call maybe_create_custom_deallocator_set,
1440 memoizing the result. */
1441 if (custom_deallocator_set **slot
1442 = m_custom_deallocator_set_cache.get (allocator_fndecl))
1443 return *slot;
1444 custom_deallocator_set *set
1445 = maybe_create_custom_deallocator_set (allocator_fndecl);
1446 m_custom_deallocator_set_cache.put (allocator_fndecl, set);
1447 return set;
1448}
1449
1450/* Given ALLOCATOR_FNDECL, a FUNCTION_DECL with attributes,
1451 look for any "__attribute__((malloc(FOO)))" and return a
1452 custom_deallocator_set for them, consolidating them
1453 to ensure uniqueness of the sets.
1454
1455 Return NULL if it has no such attributes.
1456
1457 Subroutine of get_or_create_custom_deallocator_set which
1458 memoizes the result. */
1459
1460custom_deallocator_set *
1461malloc_state_machine::
1462maybe_create_custom_deallocator_set (tree allocator_fndecl)
1463{
1464 tree attrs = DECL_ATTRIBUTES (allocator_fndecl);
1465 gcc_assert (attrs);
1466
1467 /* Look for instances of __attribute__((malloc(FOO))). */
1468 auto_vec<const deallocator *> deallocator_vec;
1469 for (tree allocs = attrs;
1470 (allocs = lookup_attribute ("malloc", allocs));
1471 allocs = TREE_CHAIN (allocs))
1472 {
1473 tree args = TREE_VALUE (allocs);
1474 if (!args)
1475 continue;
1476 if (TREE_VALUE (args))
1477 {
1478 const deallocator *d
1479 = get_or_create_deallocator (TREE_VALUE (args));
1480 deallocator_vec.safe_push (d);
1481 }
1482 }
1483
1484 /* If there weren't any deallocators, bail. */
1485 if (deallocator_vec.length () == 0)
1486 return NULL;
1487
1488 /* Consolidate, so that we reuse existing deallocator_set
1489 instances. */
1490 deallocator_vec.qsort (deallocator::cmp_ptr_ptr);
1491 custom_deallocator_set **slot
1492 = m_custom_deallocator_set_map.get (&deallocator_vec);
1493 if (slot)
1494 return *slot;
1495 custom_deallocator_set *set
1496 = new custom_deallocator_set (this, &deallocator_vec, WORDING_DEALLOCATED);
1497 m_custom_deallocator_set_map.put (&set->m_deallocator_vec, set);
1498 m_dynamic_sets.safe_push (set);
1499 return set;
1500}
1501
1502/* Get the deallocator for DEALLOCATOR_FNDECL, creating it if necessary. */
1503
1504const deallocator *
1505malloc_state_machine::get_or_create_deallocator (tree deallocator_fndecl)
1506{
1507 deallocator **slot = m_deallocator_map.get (deallocator_fndecl);
1508 if (slot)
1509 return *slot;
1510
1511 /* Reuse "free". */
1512 deallocator *d;
1513 if (is_named_call_p (deallocator_fndecl, "free")
84606efb
SP
1514 || is_std_named_call_p (deallocator_fndecl, "free")
1515 || is_named_call_p (deallocator_fndecl, "__builtin_free"))
c7e276b8
DM
1516 d = &m_free.m_deallocator;
1517 else
1518 {
1519 custom_deallocator *cd
1520 = new custom_deallocator (this, deallocator_fndecl,
1521 WORDING_DEALLOCATED);
1522 m_dynamic_deallocators.safe_push (cd);
1523 d = cd;
1524 }
1525 m_deallocator_map.put (deallocator_fndecl, d);
1526 return d;
757bf1df
DM
1527}
1528
31534ac2
SP
1529/* Try to identify the function declaration either by name or as a known malloc
1530 builtin. */
1531
1532static bool
1533known_allocator_p (const_tree fndecl, const gcall *call)
1534{
1535 /* Either it is a function we know by name and number of arguments... */
1536 if (is_named_call_p (fndecl, "malloc", call, 1)
1537 || is_named_call_p (fndecl, "calloc", call, 2)
1538 || is_std_named_call_p (fndecl, "malloc", call, 1)
1539 || is_std_named_call_p (fndecl, "calloc", call, 2)
1540 || is_named_call_p (fndecl, "strdup", call, 1)
1541 || is_named_call_p (fndecl, "strndup", call, 2))
1542 return true;
1543
1544 /* ... or it is a builtin allocator that allocates objects freed with
1545 __builtin_free. */
1546 if (fndecl_built_in_p (fndecl))
1547 switch (DECL_FUNCTION_CODE (fndecl))
1548 {
1549 case BUILT_IN_MALLOC:
1550 case BUILT_IN_CALLOC:
1551 case BUILT_IN_STRDUP:
1552 case BUILT_IN_STRNDUP:
1553 return true;
1554 default:
1555 break;
1556 }
1557
1558 return false;
1559}
1560
757bf1df
DM
1561/* Implementation of state_machine::on_stmt vfunc for malloc_state_machine. */
1562
1563bool
1564malloc_state_machine::on_stmt (sm_context *sm_ctxt,
1565 const supernode *node,
1566 const gimple *stmt) const
1567{
1568 if (const gcall *call = dyn_cast <const gcall *> (stmt))
1569 if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call))
1570 {
31534ac2 1571 if (known_allocator_p (callee_fndecl, call))
757bf1df 1572 {
c7e276b8 1573 on_allocator_call (sm_ctxt, call, &m_free);
1690a839
DM
1574 return true;
1575 }
1576
1577 if (is_named_call_p (callee_fndecl, "operator new", call, 1))
c7e276b8 1578 on_allocator_call (sm_ctxt, call, &m_scalar_delete);
1690a839 1579 else if (is_named_call_p (callee_fndecl, "operator new []", call, 1))
c7e276b8 1580 on_allocator_call (sm_ctxt, call, &m_vector_delete);
1690a839
DM
1581 else if (is_named_call_p (callee_fndecl, "operator delete", call, 1)
1582 || is_named_call_p (callee_fndecl, "operator delete", call, 2))
1583 {
c7e276b8
DM
1584 on_deallocator_call (sm_ctxt, node, call,
1585 &m_scalar_delete.m_deallocator, 0);
1690a839
DM
1586 return true;
1587 }
1588 else if (is_named_call_p (callee_fndecl, "operator delete []", call, 1))
1589 {
c7e276b8
DM
1590 on_deallocator_call (sm_ctxt, node, call,
1591 &m_vector_delete.m_deallocator, 0);
757bf1df
DM
1592 return true;
1593 }
1594
1595 if (is_named_call_p (callee_fndecl, "alloca", call, 1)
1596 || is_named_call_p (callee_fndecl, "__builtin_alloca", call, 1))
1597 {
1598 tree lhs = gimple_call_lhs (call);
1599 if (lhs)
808f4dfe 1600 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_non_heap);
757bf1df
DM
1601 return true;
1602 }
1603
1604 if (is_named_call_p (callee_fndecl, "free", call, 1)
9f00b22f 1605 || is_std_named_call_p (callee_fndecl, "free", call, 1)
757bf1df
DM
1606 || is_named_call_p (callee_fndecl, "__builtin_free", call, 1))
1607 {
c7e276b8
DM
1608 on_deallocator_call (sm_ctxt, node, call,
1609 &m_free.m_deallocator, 0);
757bf1df
DM
1610 return true;
1611 }
1612
a6baafca
DM
1613 if (is_named_call_p (callee_fndecl, "realloc", call, 2)
1614 || is_named_call_p (callee_fndecl, "__builtin_realloc", call, 2))
1615 {
1616 on_realloc_call (sm_ctxt, node, call);
1617 return true;
1618 }
1619
cd323d97
DM
1620 if (unaffected_by_call_p (callee_fndecl))
1621 return true;
1622
c7e276b8
DM
1623 /* Cast away const-ness for cache-like operations. */
1624 malloc_state_machine *mutable_this
1625 = const_cast <malloc_state_machine *> (this);
1626
1627 /* Handle "__attribute__((malloc(FOO)))". */
1628 if (const deallocator_set *deallocators
1629 = mutable_this->get_or_create_custom_deallocator_set
1630 (callee_fndecl))
1631 {
1632 tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (callee_fndecl));
1633 bool returns_nonnull
1634 = lookup_attribute ("returns_nonnull", attrs);
1635 on_allocator_call (sm_ctxt, call, deallocators, returns_nonnull);
1636 }
1637
757bf1df
DM
1638 /* Handle "__attribute__((nonnull))". */
1639 {
1640 tree fntype = TREE_TYPE (callee_fndecl);
1641 bitmap nonnull_args = get_nonnull_args (fntype);
1642 if (nonnull_args)
1643 {
1644 for (unsigned i = 0; i < gimple_call_num_args (stmt); i++)
1645 {
1646 tree arg = gimple_call_arg (stmt, i);
1647 if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
1648 continue;
1649 /* If we have a nonnull-args, and either all pointers, or just
1650 the specified pointers. */
1651 if (bitmap_empty_p (nonnull_args)
1652 || bitmap_bit_p (nonnull_args, i))
1653 {
25ef215a
DM
1654 state_t state = sm_ctxt->get_state (stmt, arg);
1655 /* Can't use a switch as the states are non-const. */
1690a839 1656 if (unchecked_p (state))
25ef215a 1657 {
7d8f4240 1658 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
25ef215a
DM
1659 sm_ctxt->warn (node, stmt, arg,
1660 new possible_null_arg (*this, diag_arg,
1661 callee_fndecl,
1662 i));
1690a839
DM
1663 const allocation_state *astate
1664 = as_a_allocation_state (state);
1665 sm_ctxt->set_next_state (stmt, arg,
1666 astate->get_nonnull ());
25ef215a
DM
1667 }
1668 else if (state == m_null)
1669 {
7d8f4240 1670 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
25ef215a
DM
1671 sm_ctxt->warn (node, stmt, arg,
1672 new null_arg (*this, diag_arg,
1673 callee_fndecl, i));
1674 sm_ctxt->set_next_state (stmt, arg, m_stop);
1675 }
757bf1df
DM
1676 }
1677 }
1678 BITMAP_FREE (nonnull_args);
1679 }
1680 }
c7e276b8
DM
1681
1682 /* Check for this after nonnull, so that if we have both
1683 then we transition to "freed", rather than "checked". */
1684 unsigned dealloc_argno = fndecl_dealloc_argno (callee_fndecl);
1685 if (dealloc_argno != UINT_MAX)
1686 {
1687 const deallocator *d
1688 = mutable_this->get_or_create_deallocator (callee_fndecl);
1689 on_deallocator_call (sm_ctxt, node, call, d, dealloc_argno);
1690 }
757bf1df
DM
1691 }
1692
808f4dfe 1693 if (tree lhs = sm_ctxt->is_zero_assignment (stmt))
8525d1f5 1694 if (any_pointer_p (lhs))
1690a839 1695 on_zero_assignment (sm_ctxt, stmt,lhs);
757bf1df 1696
808f4dfe
DM
1697 /* If we have "LHS = &EXPR;" and EXPR is something other than a MEM_REF,
1698 transition LHS from start to non_heap.
1699 Doing it for ADDR_EXPR(MEM_REF()) is likely wrong, and can lead to
1700 unbounded chains of unmergeable sm-state on pointer arithmetic in loops
1701 when optimization is enabled. */
757bf1df
DM
1702 if (const gassign *assign_stmt = dyn_cast <const gassign *> (stmt))
1703 {
1704 enum tree_code op = gimple_assign_rhs_code (assign_stmt);
1705 if (op == ADDR_EXPR)
1706 {
1707 tree lhs = gimple_assign_lhs (assign_stmt);
1708 if (lhs)
1709 {
808f4dfe
DM
1710 tree addr_expr = gimple_assign_rhs1 (assign_stmt);
1711 if (TREE_CODE (TREE_OPERAND (addr_expr, 0)) != MEM_REF)
1712 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_non_heap);
757bf1df
DM
1713 }
1714 }
1715 }
1716
1717 /* Handle dereferences. */
1718 for (unsigned i = 0; i < gimple_num_ops (stmt); i++)
1719 {
1720 tree op = gimple_op (stmt, i);
1721 if (!op)
1722 continue;
1723 if (TREE_CODE (op) == COMPONENT_REF)
1724 op = TREE_OPERAND (op, 0);
1725
1726 if (TREE_CODE (op) == MEM_REF)
1727 {
1728 tree arg = TREE_OPERAND (op, 0);
757bf1df 1729
25ef215a 1730 state_t state = sm_ctxt->get_state (stmt, arg);
1690a839 1731 if (unchecked_p (state))
25ef215a 1732 {
0f9aa35c 1733 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
25ef215a
DM
1734 sm_ctxt->warn (node, stmt, arg,
1735 new possible_null_deref (*this, diag_arg));
1690a839
DM
1736 const allocation_state *astate = as_a_allocation_state (state);
1737 sm_ctxt->set_next_state (stmt, arg, astate->get_nonnull ());
25ef215a
DM
1738 }
1739 else if (state == m_null)
1740 {
0f9aa35c 1741 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
25ef215a
DM
1742 sm_ctxt->warn (node, stmt, arg,
1743 new null_deref (*this, diag_arg));
1744 sm_ctxt->set_next_state (stmt, arg, m_stop);
1745 }
1690a839 1746 else if (freed_p (state))
25ef215a 1747 {
0f9aa35c 1748 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1690a839 1749 const allocation_state *astate = as_a_allocation_state (state);
25ef215a 1750 sm_ctxt->warn (node, stmt, arg,
1690a839 1751 new use_after_free (*this, diag_arg,
c7e276b8 1752 astate->m_deallocator));
25ef215a
DM
1753 sm_ctxt->set_next_state (stmt, arg, m_stop);
1754 }
757bf1df
DM
1755 }
1756 }
1757 return false;
1758}
1759
c7e276b8
DM
1760/* Handle a call to an allocator.
1761 RETURNS_NONNULL is true if CALL is to a fndecl known to have
1762 __attribute__((returns_nonnull)). */
1690a839
DM
1763
1764void
1765malloc_state_machine::on_allocator_call (sm_context *sm_ctxt,
1766 const gcall *call,
c7e276b8
DM
1767 const deallocator_set *deallocators,
1768 bool returns_nonnull) const
1690a839
DM
1769{
1770 tree lhs = gimple_call_lhs (call);
1771 if (lhs)
1772 {
1773 if (sm_ctxt->get_state (call, lhs) == m_start)
c7e276b8
DM
1774 sm_ctxt->set_next_state (call, lhs,
1775 (returns_nonnull
1776 ? deallocators->m_nonnull
1777 : deallocators->m_unchecked));
1690a839
DM
1778 }
1779 else
1780 {
1781 /* TODO: report leak. */
1782 }
1783}
1784
1785void
1786malloc_state_machine::on_deallocator_call (sm_context *sm_ctxt,
1787 const supernode *node,
1788 const gcall *call,
c7e276b8
DM
1789 const deallocator *d,
1790 unsigned argno) const
1690a839 1791{
c7e276b8
DM
1792 if (argno >= gimple_call_num_args (call))
1793 return;
1794 tree arg = gimple_call_arg (call, argno);
1690a839
DM
1795
1796 state_t state = sm_ctxt->get_state (call, arg);
1797
1798 /* start/unchecked/nonnull -> freed. */
1799 if (state == m_start)
c7e276b8 1800 sm_ctxt->set_next_state (call, arg, d->m_freed);
1690a839
DM
1801 else if (unchecked_p (state) || nonnull_p (state))
1802 {
1803 const allocation_state *astate = as_a_allocation_state (state);
c7e276b8
DM
1804 gcc_assert (astate->m_deallocators);
1805 if (!astate->m_deallocators->contains_p (d))
1690a839
DM
1806 {
1807 /* Wrong allocator. */
0f9aa35c 1808 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
c7e276b8 1809 pending_diagnostic *pd
1690a839 1810 = new mismatching_deallocation (*this, diag_arg,
c7e276b8
DM
1811 astate->m_deallocators,
1812 d);
1813 sm_ctxt->warn (node, call, arg, pd);
1690a839 1814 }
c7e276b8 1815 sm_ctxt->set_next_state (call, arg, d->m_freed);
1690a839
DM
1816 }
1817
1818 /* Keep state "null" as-is, rather than transitioning to "freed";
1819 we don't want to complain about double-free of NULL. */
c7e276b8 1820 else if (state == d->m_freed)
1690a839
DM
1821 {
1822 /* freed -> stop, with warning. */
0f9aa35c 1823 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1690a839 1824 sm_ctxt->warn (node, call, arg,
c7e276b8 1825 new double_free (*this, diag_arg, d->m_name));
1690a839
DM
1826 sm_ctxt->set_next_state (call, arg, m_stop);
1827 }
1828 else if (state == m_non_heap)
1829 {
1830 /* non-heap -> stop, with warning. */
0f9aa35c 1831 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1690a839
DM
1832 sm_ctxt->warn (node, call, arg,
1833 new free_of_non_heap (*this, diag_arg,
c7e276b8 1834 d->m_name));
1690a839
DM
1835 sm_ctxt->set_next_state (call, arg, m_stop);
1836 }
1837}
1838
a6baafca
DM
1839/* Implementation of realloc(3):
1840
1841 void *realloc(void *ptr, size_t size);
1842
1843 realloc(3) is awkward.
1844
1845 We currently don't have a way to express multiple possible outcomes
1846 from a function call, "bifurcating" the state such as:
1847 - success: non-NULL is returned
1848 - failure: NULL is returned, existing buffer is not freed.
1849 or even an N-way state split e.g.:
1850 - buffer grew successfully in-place
1851 - buffer was successfully moved to a larger allocation
1852 - buffer was successfully contracted
1853 - realloc failed, returning NULL, without freeing existing buffer.
1854 (PR analyzer/99260 tracks this)
1855
1856 Given that we can currently only express one outcome, eliminate
1857 false positives by dropping state from the buffer. */
1858
1859void
1860malloc_state_machine::on_realloc_call (sm_context *sm_ctxt,
1861 const supernode *node ATTRIBUTE_UNUSED,
1862 const gcall *call) const
1863{
1864 tree ptr = gimple_call_arg (call, 0);
a6baafca
DM
1865
1866 state_t state = sm_ctxt->get_state (call, ptr);
1867
1868 /* Detect mismatches. */
1869 if (unchecked_p (state) || nonnull_p (state))
1870 {
1871 const allocation_state *astate = as_a_allocation_state (state);
1872 gcc_assert (astate->m_deallocators);
1873 if (astate->m_deallocators != &m_free)
1874 {
1875 /* Wrong allocator. */
0f9aa35c 1876 tree diag_ptr = sm_ctxt->get_diagnostic_tree (ptr);
a6baafca
DM
1877 pending_diagnostic *pd
1878 = new mismatching_deallocation (*this, diag_ptr,
1879 astate->m_deallocators,
1880 &m_realloc);
1881 sm_ctxt->warn (node, call, ptr, pd);
1882 }
1883 }
1884
1885 /* Transition ptr to "stop" state. */
1886 sm_ctxt->set_next_state (call, ptr, m_stop);
1887}
1888
8525d1f5
DM
1889/* Implementation of state_machine::on_phi vfunc for malloc_state_machine. */
1890
1891void
1892malloc_state_machine::on_phi (sm_context *sm_ctxt,
1690a839 1893 const supernode *node ATTRIBUTE_UNUSED,
8525d1f5
DM
1894 const gphi *phi,
1895 tree rhs) const
1896{
1897 if (zerop (rhs))
1898 {
1899 tree lhs = gimple_phi_result (phi);
1690a839 1900 on_zero_assignment (sm_ctxt, phi, lhs);
8525d1f5
DM
1901 }
1902}
1903
757bf1df
DM
1904/* Implementation of state_machine::on_condition vfunc for malloc_state_machine.
1905 Potentially transition state 'unchecked' to 'nonnull' or to 'null'. */
1906
1907void
1908malloc_state_machine::on_condition (sm_context *sm_ctxt,
1690a839 1909 const supernode *node ATTRIBUTE_UNUSED,
757bf1df 1910 const gimple *stmt,
48e8a7a6 1911 const svalue *lhs,
757bf1df 1912 enum tree_code op,
48e8a7a6 1913 const svalue *rhs) const
757bf1df 1914{
48e8a7a6 1915 if (!rhs->all_zeroes_p ())
757bf1df
DM
1916 return;
1917
1918 if (!any_pointer_p (lhs))
1919 return;
1920 if (!any_pointer_p (rhs))
1921 return;
1922
1923 if (op == NE_EXPR)
1924 {
1925 log ("got 'ARG != 0' match");
1690a839
DM
1926 state_t s = sm_ctxt->get_state (stmt, lhs);
1927 if (unchecked_p (s))
1928 {
1929 const allocation_state *astate = as_a_allocation_state (s);
1930 sm_ctxt->set_next_state (stmt, lhs, astate->get_nonnull ());
1931 }
757bf1df
DM
1932 }
1933 else if (op == EQ_EXPR)
1934 {
1935 log ("got 'ARG == 0' match");
1690a839
DM
1936 state_t s = sm_ctxt->get_state (stmt, lhs);
1937 if (unchecked_p (s))
1938 sm_ctxt->set_next_state (stmt, lhs, m_null);
757bf1df
DM
1939 }
1940}
1941
1942/* Implementation of state_machine::can_purge_p vfunc for malloc_state_machine.
1943 Don't allow purging of pointers in state 'unchecked' or 'nonnull'
1944 (to avoid false leak reports). */
1945
1946bool
1947malloc_state_machine::can_purge_p (state_t s) const
1948{
1690a839
DM
1949 enum resource_state rs = get_rs (s);
1950 return rs != RS_UNCHECKED && rs != RS_NONNULL;
757bf1df
DM
1951}
1952
1953/* Implementation of state_machine::on_leak vfunc for malloc_state_machine
1954 (for complaining about leaks of pointers in state 'unchecked' and
1955 'nonnull'). */
1956
1957pending_diagnostic *
1958malloc_state_machine::on_leak (tree var) const
1959{
1960 return new malloc_leak (*this, var);
1961}
1962
808f4dfe
DM
1963/* Implementation of state_machine::reset_when_passed_to_unknown_fn_p vfunc
1964 for malloc_state_machine. */
1965
1966bool
1967malloc_state_machine::reset_when_passed_to_unknown_fn_p (state_t s,
1968 bool is_mutable) const
1969{
1970 /* An on-stack ptr doesn't stop being stack-allocated when passed to an
1971 unknown fn. */
1972 if (s == m_non_heap)
1973 return false;
1974
1975 /* Otherwise, pointers passed as non-const can be freed. */
1976 return is_mutable;
1977}
1978
cd323d97
DM
1979/* Return true if calls to FNDECL are known to not affect this sm-state. */
1980
1981bool
1982malloc_state_machine::unaffected_by_call_p (tree fndecl)
1983{
1984 /* A set of functions that are known to not affect allocation
1985 status, even if we haven't fully modelled the rest of their
1986 behavior yet. */
1987 static const char * const funcnames[] = {
1988 /* This array must be kept sorted. */
1989 "strsep",
1990 };
1991 const size_t count
1992 = sizeof(funcnames) / sizeof (funcnames[0]);
1993 function_set fs (funcnames, count);
1994
1995 if (fs.contains_decl_p (fndecl))
1996 return true;
1997
1998 return false;
1999}
2000
8525d1f5
DM
2001/* Shared logic for handling GIMPLE_ASSIGNs and GIMPLE_PHIs that
2002 assign zero to LHS. */
2003
2004void
2005malloc_state_machine::on_zero_assignment (sm_context *sm_ctxt,
8525d1f5
DM
2006 const gimple *stmt,
2007 tree lhs) const
2008{
1690a839
DM
2009 state_t s = sm_ctxt->get_state (stmt, lhs);
2010 enum resource_state rs = get_rs (s);
2011 if (rs == RS_START
2012 || rs == RS_UNCHECKED
2013 || rs == RS_NONNULL
2014 || rs == RS_FREED)
2015 sm_ctxt->set_next_state (stmt, lhs, m_null);
8525d1f5
DM
2016}
2017
757bf1df
DM
2018} // anonymous namespace
2019
2020/* Internal interface to this file. */
2021
2022state_machine *
2023make_malloc_state_machine (logger *logger)
2024{
2025 return new malloc_state_machine (logger);
2026}
2027
75038aa6
DM
2028} // namespace ana
2029
757bf1df 2030#endif /* #if ENABLE_ANALYZER */