Line data Source code
1 : /* A state machine for detecting misuses of the malloc/free API.
2 : Copyright (C) 2019-2026 Free Software Foundation, Inc.
3 : Contributed by David Malcolm <dmalcolm@redhat.com>.
4 :
5 : This file is part of GCC.
6 :
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.
11 :
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.
16 :
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/>. */
20 :
21 : #include "analyzer/common.h"
22 :
23 : #include "diagnostics/event-id.h"
24 : #include "stringpool.h"
25 : #include "attribs.h"
26 : #include "xml-printer.h"
27 : #include "target.h"
28 :
29 : #include "analyzer/analyzer-logging.h"
30 : #include "analyzer/sm.h"
31 : #include "analyzer/pending-diagnostic.h"
32 : #include "analyzer/call-string.h"
33 : #include "analyzer/program-point.h"
34 : #include "analyzer/store.h"
35 : #include "analyzer/region-model.h"
36 : #include "analyzer/call-details.h"
37 : #include "analyzer/function-set.h"
38 : #include "analyzer/program-state.h"
39 : #include "analyzer/checker-event.h"
40 : #include "analyzer/exploded-graph.h"
41 : #include "analyzer/inlining-iterator.h"
42 : #include "analyzer/ana-state-to-diagnostic-state.h"
43 :
44 : #if ENABLE_ANALYZER
45 :
46 : namespace ana {
47 :
48 : namespace {
49 :
50 : /* This state machine and its various support classes track allocations
51 : and deallocations.
52 :
53 : It has a few standard allocation/deallocation pairs (e.g. new/delete),
54 : and also supports user-defined ones via
55 : __attribute__ ((malloc(DEALLOCATOR))).
56 :
57 : There can be more than one valid deallocator for a given allocator,
58 : for example:
59 : __attribute__ ((malloc (fclose)))
60 : __attribute__ ((malloc (freopen, 3)))
61 : FILE* fopen (const char*, const char*);
62 : A deallocator_set represents a particular set of valid deallocators.
63 :
64 : We track the expected deallocator_set for a value, but not the allocation
65 : function - there could be more than one allocator per deallocator_set.
66 : For example, there could be dozens of allocators for "free" beyond just
67 : malloc e.g. calloc, xstrdup, etc. We don't want to explode the number
68 : of states by tracking individual allocators in the exploded graph;
69 : we merely want to track "this value expects to have 'free' called on it".
70 : Perhaps we can reconstruct which allocator was used later, when emitting
71 : the path, if it's necessary for precision of wording of diagnostics. */
72 :
73 : class deallocator;
74 : class deallocator_set;
75 : class malloc_state_machine;
76 :
77 : /* An enum for discriminating between different kinds of allocation_state. */
78 :
79 : enum resource_state
80 : {
81 : /* States that are independent of allocator/deallocator. */
82 :
83 : /* The start state. */
84 : RS_START,
85 :
86 : /* State for a pointer that's been unconditionally dereferenced. */
87 : RS_ASSUMED_NON_NULL,
88 :
89 : /* State for a pointer that's known to be NULL. */
90 : RS_NULL,
91 :
92 : /* State for a pointer that's known to not be on the heap (e.g. to a local
93 : or global). */
94 : RS_NON_HEAP,
95 :
96 : /* Stop state, for pointers we don't want to track any more. */
97 : RS_STOP,
98 :
99 : /* States that relate to a specific deallocator_set. */
100 :
101 : /* State for a pointer returned from an allocator that hasn't
102 : been checked for NULL.
103 : It could be a pointer to heap-allocated memory, or could be NULL. */
104 : RS_UNCHECKED,
105 :
106 : /* State for a pointer returned from an allocator,
107 : known to be non-NULL. */
108 : RS_NONNULL,
109 :
110 : /* State for a pointer passed to a deallocator. */
111 : RS_FREED
112 : };
113 :
114 : /* Custom state subclass, which can optionally refer to an a
115 : deallocator_set. */
116 :
117 : struct allocation_state : public state_machine::state
118 : {
119 46814 : allocation_state (const char *name, unsigned id,
120 : enum resource_state rs,
121 : const deallocator_set *deallocators,
122 : const deallocator *deallocator)
123 46814 : : state (name, id), m_rs (rs),
124 44277 : m_deallocators (deallocators),
125 44277 : m_deallocator (deallocator)
126 : {}
127 :
128 : void dump_to_pp (pretty_printer *pp) const override;
129 :
130 : const allocation_state *get_nonnull () const;
131 :
132 : enum resource_state m_rs;
133 : const deallocator_set *m_deallocators;
134 : const deallocator *m_deallocator;
135 : };
136 :
137 : /* Custom state subclass, for the "assumed-non-null" state
138 : where the assumption happens in a particular frame. */
139 :
140 : struct assumed_non_null_state : public allocation_state
141 : {
142 2537 : assumed_non_null_state (const char *name, unsigned id,
143 : const frame_region *frame)
144 2537 : : allocation_state (name, id, RS_ASSUMED_NON_NULL,
145 : nullptr, nullptr),
146 2537 : m_frame (frame)
147 : {
148 2537 : gcc_assert (m_frame);
149 2537 : }
150 :
151 : void dump_to_pp (pretty_printer *pp) const final override;
152 :
153 : const frame_region *m_frame;
154 : };
155 :
156 : /* An enum for choosing which wording to use in various diagnostics
157 : when describing deallocations. */
158 :
159 : enum wording
160 : {
161 : WORDING_FREED,
162 : WORDING_DELETED,
163 : WORDING_DEALLOCATED,
164 : WORDING_REALLOCATED
165 : };
166 :
167 : /* Base class representing a deallocation function,
168 : either a built-in one we know about, or one exposed via
169 : __attribute__((malloc(DEALLOCATOR))). */
170 :
171 : struct deallocator
172 : {
173 : hashval_t hash () const;
174 : void dump_to_pp (pretty_printer *pp) const;
175 : static int cmp (const deallocator *a, const deallocator *b);
176 : static int cmp_ptr_ptr (const void *, const void *);
177 :
178 : /* Name to use in diagnostics. */
179 : const char *m_name;
180 :
181 : /* Which wording to use in diagnostics. */
182 : enum wording m_wording;
183 :
184 : /* State for a value passed to one of the deallocators. */
185 : state_machine::state_t m_freed;
186 :
187 : protected:
188 : deallocator (malloc_state_machine *sm,
189 : const char *name,
190 : enum wording wording);
191 : };
192 :
193 : /* Subclass representing a predefined deallocator.
194 : e.g. "delete []", without needing a specific FUNCTION_DECL
195 : ahead of time. */
196 :
197 : struct standard_deallocator : public deallocator
198 : {
199 : standard_deallocator (malloc_state_machine *sm,
200 : const char *name,
201 : enum wording wording);
202 : };
203 :
204 : /* Subclass representing a user-defined deallocator
205 : via __attribute__((malloc(DEALLOCATOR))) given
206 : a specific FUNCTION_DECL. */
207 :
208 : struct custom_deallocator : public deallocator
209 : {
210 70 : custom_deallocator (malloc_state_machine *sm,
211 : tree deallocator_fndecl,
212 : enum wording wording)
213 70 : : deallocator (sm, IDENTIFIER_POINTER (DECL_NAME (deallocator_fndecl)),
214 140 : wording)
215 : {
216 70 : }
217 : };
218 :
219 : /* Base class representing a set of possible deallocators.
220 : Often this will be just a single deallocator, but some
221 : allocators have multiple valid deallocators (e.g. the result of
222 : "fopen" can be closed by either "fclose" or "freopen"). */
223 :
224 : struct deallocator_set
225 : {
226 : deallocator_set (malloc_state_machine *sm,
227 : enum wording wording);
228 3389 : virtual ~deallocator_set () {}
229 :
230 : virtual bool contains_p (const deallocator *d) const = 0;
231 : virtual const deallocator *maybe_get_single () const = 0;
232 : virtual void dump_to_pp (pretty_printer *pp) const = 0;
233 : void dump () const;
234 :
235 : /* Which wording to use in diagnostics. */
236 : enum wording m_wording;
237 :
238 : /* Pointers to states.
239 : These states are owned by the state_machine base class. */
240 :
241 : /* State for an unchecked result from an allocator using this set. */
242 : state_machine::state_t m_unchecked;
243 :
244 : /* State for a known non-NULL result from such an allocator. */
245 : state_machine::state_t m_nonnull;
246 : };
247 :
248 : /* Subclass of deallocator_set representing a set of deallocators
249 : defined by one or more __attribute__((malloc(DEALLOCATOR))). */
250 :
251 : struct custom_deallocator_set : public deallocator_set
252 : {
253 : typedef const auto_vec <const deallocator *> *key_t;
254 :
255 : custom_deallocator_set (malloc_state_machine *sm,
256 : const auto_vec <const deallocator *> *vec,
257 : //const char *name,
258 : //const char *dealloc_funcname,
259 : //unsigned arg_idx,
260 : enum wording wording);
261 :
262 : bool contains_p (const deallocator *d) const final override;
263 : const deallocator *maybe_get_single () const final override;
264 : void dump_to_pp (pretty_printer *pp) const final override;
265 :
266 : auto_vec <const deallocator *> m_deallocator_vec;
267 : };
268 :
269 : /* Subclass of deallocator_set representing a set of deallocators
270 : with a single standard_deallocator, e.g. "delete []". */
271 :
272 3389 : struct standard_deallocator_set : public deallocator_set
273 : {
274 : standard_deallocator_set (malloc_state_machine *sm,
275 : const char *name,
276 : enum wording wording);
277 :
278 : bool contains_p (const deallocator *d) const final override;
279 : const deallocator *maybe_get_single () const final override;
280 : void dump_to_pp (pretty_printer *pp) const final override;
281 :
282 : standard_deallocator m_deallocator;
283 : };
284 :
285 : /* Traits class for ensuring uniqueness of deallocator_sets within
286 : malloc_state_machine. */
287 :
288 : struct deallocator_set_map_traits
289 : {
290 : typedef custom_deallocator_set::key_t key_type;
291 : typedef custom_deallocator_set *value_type;
292 : typedef custom_deallocator_set *compare_type;
293 :
294 222 : static inline hashval_t hash (const key_type &k)
295 : {
296 222 : gcc_assert (k != nullptr);
297 222 : gcc_assert (k != reinterpret_cast<key_type> (1));
298 :
299 : hashval_t result = 0;
300 : unsigned i;
301 : const deallocator *d;
302 500 : FOR_EACH_VEC_ELT (*k, i, d)
303 278 : result ^= d->hash ();
304 222 : return result;
305 : }
306 69 : static inline bool equal_keys (const key_type &k1, const key_type &k2)
307 : {
308 207 : if (k1->length () != k2->length ())
309 : return false;
310 :
311 58 : for (unsigned i = 0; i < k1->length (); i++)
312 45 : if ((*k1)[i] != (*k2)[i])
313 : return false;
314 :
315 : return true;
316 : }
317 : template <typename T>
318 : static inline void remove (T &)
319 : {
320 : /* empty; the nodes are handled elsewhere. */
321 : }
322 : template <typename T>
323 : static inline void mark_deleted (T &entry)
324 : {
325 : entry.m_key = reinterpret_cast<key_type> (1);
326 : }
327 : template <typename T>
328 44057 : static inline void mark_empty (T &entry)
329 : {
330 44057 : entry.m_key = nullptr;
331 : }
332 : template <typename T>
333 157 : static inline bool is_deleted (const T &entry)
334 : {
335 157 : return entry.m_key == reinterpret_cast<key_type> (1);
336 : }
337 : template <typename T>
338 46330 : static inline bool is_empty (const T &entry)
339 : {
340 46330 : return entry.m_key == nullptr;
341 : }
342 : static const bool empty_zero_p = false;
343 : };
344 :
345 : /* A state machine for detecting misuses of the malloc/free API.
346 :
347 : See sm-malloc.dot for an overview (keep this in-sync with that file). */
348 :
349 : class malloc_state_machine : public state_machine
350 : {
351 : public:
352 : typedef allocation_state custom_data_t;
353 :
354 : malloc_state_machine (logger *logger);
355 : ~malloc_state_machine ();
356 :
357 : state_t
358 : add_state (const char *name, enum resource_state rs,
359 : const deallocator_set *deallocators,
360 : const deallocator *deallocator);
361 :
362 1821575 : bool inherited_state_p () const final override { return false; }
363 :
364 : state_machine::state_t
365 1735324 : get_default_state (const svalue *sval) const final override
366 : {
367 1735324 : if (tree cst = sval->maybe_get_constant ())
368 : {
369 382929 : if (zerop (cst))
370 122909 : return m_null;
371 : }
372 1612415 : if (const region_svalue *ptr = sval->dyn_cast_region_svalue ())
373 : {
374 137325 : const region *reg = ptr->get_pointee ();
375 137325 : switch (reg->get_memory_space ())
376 : {
377 : default:
378 : break;
379 32936 : case MEMSPACE_CODE:
380 32936 : case MEMSPACE_GLOBALS:
381 32936 : case MEMSPACE_STACK:
382 32936 : case MEMSPACE_READONLY_DATA:
383 32936 : return m_non_heap;
384 : }
385 : }
386 1579479 : return m_start;
387 : }
388 :
389 : bool on_stmt (sm_context &sm_ctxt,
390 : const gimple *stmt) const final override;
391 :
392 : void on_phi (sm_context &sm_ctxt,
393 : const gphi *phi,
394 : tree rhs) const final override;
395 :
396 : void
397 : check_call_preconditions (sm_context &sm_ctxt,
398 : const call_details &cd) const final override;
399 :
400 : void on_condition (sm_context &sm_ctxt,
401 : const svalue *lhs,
402 : enum tree_code op,
403 : const svalue *rhs) const final override;
404 :
405 : void on_pop_frame (sm_state_map *smap,
406 : const frame_region *) const final override;
407 :
408 : bool can_purge_p (state_t s) const final override;
409 :
410 : std::unique_ptr<pending_diagnostic>
411 : on_leak (tree var,
412 : const program_state *old_state,
413 : const program_state *new_state) const final override;
414 :
415 : bool reset_when_passed_to_unknown_fn_p (state_t s,
416 : bool is_mutable) const final override;
417 :
418 : state_t
419 : maybe_get_merged_states_nonequal (state_t state_a,
420 : state_t state_b) const final override;
421 :
422 : static bool unaffected_by_call_p (tree fndecl);
423 :
424 : void maybe_assume_non_null (sm_context &sm_ctxt,
425 : tree ptr) const;
426 :
427 : void on_realloc_with_move (region_model *model,
428 : sm_state_map *smap,
429 : const svalue *old_ptr_sval,
430 : const svalue *new_ptr_sval,
431 : const extrinsic_state &ext_state) const;
432 :
433 : void transition_ptr_sval_non_null (region_model *model,
434 : sm_state_map *smap,
435 : const svalue *new_ptr_sval,
436 : const extrinsic_state &ext_state) const;
437 :
438 : void
439 : add_state_to_state_graph (analyzer_state_graph &out_state_graph,
440 : const svalue &sval,
441 : state_machine::state_t state) const final override;
442 :
443 : standard_deallocator_set m_free;
444 : standard_deallocator_set m_scalar_delete;
445 : standard_deallocator_set m_vector_delete;
446 :
447 : standard_deallocator m_realloc;
448 :
449 : /* States that are independent of api. */
450 :
451 : /* States for a pointer that's been unconditionally dereferenced
452 : in a particular stack frame. */
453 : hash_map<const frame_region *, state_t> m_assumed_non_null;
454 :
455 : /* State for a pointer that's known to be NULL. */
456 : state_t m_null;
457 :
458 : /* State for a pointer that's known to not be on the heap (e.g. to a local
459 : or global). */
460 : state_t m_non_heap; // TODO: or should this be a different state machine?
461 : // or do we need child values etc?
462 :
463 : /* Stop state, for pointers we don't want to track any more. */
464 : state_t m_stop;
465 :
466 : private:
467 : const custom_deallocator_set *
468 : get_or_create_custom_deallocator_set (tree allocator_fndecl);
469 : custom_deallocator_set *
470 : maybe_create_custom_deallocator_set (tree allocator_fndecl);
471 : const deallocator *
472 : get_or_create_deallocator (tree deallocator_fndecl);
473 :
474 : state_t
475 : get_or_create_assumed_non_null_state_for_frame (const frame_region *frame);
476 :
477 : void
478 : maybe_complain_about_deref_before_check (sm_context &sm_ctxt,
479 : const assumed_non_null_state *,
480 : tree ptr) const;
481 :
482 : void on_allocator_call (sm_context &sm_ctxt,
483 : const gcall &call,
484 : const deallocator_set *deallocators,
485 : bool returns_nonnull = false) const;
486 : void handle_free_of_non_heap (sm_context &sm_ctxt,
487 : const gcall &call,
488 : tree arg,
489 : const deallocator *d) const;
490 : void on_deallocator_call (sm_context &sm_ctxt,
491 : const gcall &call,
492 : const deallocator *d,
493 : unsigned argno) const;
494 : void on_realloc_call (sm_context &sm_ctxt,
495 : const gcall &call) const;
496 : void on_zero_assignment (sm_context &sm_ctxt,
497 : tree lhs) const;
498 : void handle_nonnull (sm_context &sm_ctx,
499 : tree fndecl,
500 : tree arg,
501 : unsigned i) const;
502 :
503 : /* A map for consolidating deallocators so that they are
504 : unique per deallocator FUNCTION_DECL. */
505 : typedef hash_map<tree, deallocator *> deallocator_map_t;
506 : deallocator_map_t m_deallocator_map;
507 :
508 : /* Memoized lookups from FUNCTION_DECL to custom_deallocator_set *. */
509 : typedef hash_map<tree, custom_deallocator_set *> deallocator_set_cache_t;
510 : deallocator_set_cache_t m_custom_deallocator_set_cache;
511 :
512 : /* A map for consolidating custom_deallocator_set instances. */
513 : typedef hash_map<custom_deallocator_set::key_t,
514 : custom_deallocator_set *,
515 : deallocator_set_map_traits> custom_deallocator_set_map_t;
516 : custom_deallocator_set_map_t m_custom_deallocator_set_map;
517 :
518 : /* Record of dynamically-allocated objects, for cleanup. */
519 : auto_vec <custom_deallocator_set *> m_dynamic_sets;
520 : auto_vec <custom_deallocator *> m_dynamic_deallocators;
521 : };
522 :
523 : /* struct deallocator. */
524 :
525 13626 : deallocator::deallocator (malloc_state_machine *sm,
526 : const char *name,
527 13626 : enum wording wording)
528 13626 : : m_name (name),
529 13626 : m_wording (wording),
530 13626 : m_freed (sm->add_state ("freed", RS_FREED, nullptr, this))
531 : {
532 13626 : }
533 :
534 : hashval_t
535 278 : deallocator::hash () const
536 : {
537 278 : return (hashval_t)m_freed->get_id ();
538 : }
539 :
540 : void
541 0 : deallocator::dump_to_pp (pretty_printer *pp) const
542 : {
543 0 : pp_printf (pp, "%qs", m_name);
544 0 : }
545 :
546 : int
547 80 : deallocator::cmp (const deallocator *a, const deallocator *b)
548 : {
549 80 : return (int)a->m_freed->get_id () - (int)b->m_freed->get_id ();
550 : }
551 :
552 : int
553 80 : deallocator::cmp_ptr_ptr (const void *a, const void *b)
554 : {
555 80 : return cmp (*(const deallocator * const *)a,
556 80 : *(const deallocator * const *)b);
557 : }
558 :
559 :
560 : /* struct standard_deallocator : public deallocator. */
561 :
562 13556 : standard_deallocator::standard_deallocator (malloc_state_machine *sm,
563 : const char *name,
564 13556 : enum wording wording)
565 0 : : deallocator (sm, name, wording)
566 : {
567 0 : }
568 :
569 : /* struct deallocator_set. */
570 :
571 10242 : deallocator_set::deallocator_set (malloc_state_machine *sm,
572 10242 : enum wording wording)
573 10242 : : m_wording (wording),
574 20484 : m_unchecked (sm->add_state ("unchecked", RS_UNCHECKED, this, nullptr)),
575 10242 : m_nonnull (sm->add_state ("nonnull", RS_NONNULL, this, nullptr))
576 : {
577 10242 : }
578 :
579 : /* Dump a description of this deallocator_set to stderr. */
580 :
581 : DEBUG_FUNCTION void
582 0 : deallocator_set::dump () const
583 : {
584 0 : tree_dump_pretty_printer pp (stderr);
585 0 : dump_to_pp (&pp);
586 0 : pp_newline (&pp);
587 0 : }
588 :
589 : /* struct custom_deallocator_set : public deallocator_set. */
590 :
591 75 : custom_deallocator_set::
592 : custom_deallocator_set (malloc_state_machine *sm,
593 : const auto_vec <const deallocator *> *vec,
594 75 : enum wording wording)
595 : : deallocator_set (sm, wording),
596 150 : m_deallocator_vec (vec->length ())
597 : {
598 75 : unsigned i;
599 75 : const deallocator *d;
600 233 : FOR_EACH_VEC_ELT (*vec, i, d)
601 83 : m_deallocator_vec.safe_push (d);
602 75 : }
603 :
604 : bool
605 177 : custom_deallocator_set::contains_p (const deallocator *d) const
606 : {
607 177 : unsigned i;
608 177 : const deallocator *cd;
609 286 : FOR_EACH_VEC_ELT (m_deallocator_vec, i, cd)
610 225 : if (cd == d)
611 : return true;
612 : return false;
613 : }
614 :
615 : const deallocator *
616 305 : custom_deallocator_set::maybe_get_single () const
617 : {
618 305 : if (m_deallocator_vec.length () == 1)
619 125 : return m_deallocator_vec[0];
620 : return nullptr;
621 : }
622 :
623 : void
624 0 : custom_deallocator_set::dump_to_pp (pretty_printer *pp) const
625 : {
626 0 : pp_character (pp, '{');
627 0 : unsigned i;
628 0 : const deallocator *d;
629 0 : FOR_EACH_VEC_ELT (m_deallocator_vec, i, d)
630 : {
631 0 : if (i > 0)
632 0 : pp_string (pp, ", ");
633 0 : d->dump_to_pp (pp);
634 : }
635 0 : pp_character (pp, '}');
636 0 : }
637 :
638 : /* struct standard_deallocator_set : public deallocator_set. */
639 :
640 10167 : standard_deallocator_set::standard_deallocator_set (malloc_state_machine *sm,
641 : const char *name,
642 10167 : enum wording wording)
643 : : deallocator_set (sm, wording),
644 10167 : m_deallocator (sm, name, wording)
645 : {
646 10167 : }
647 :
648 : bool
649 5441 : standard_deallocator_set::contains_p (const deallocator *d) const
650 : {
651 5441 : return d == &m_deallocator;
652 : }
653 :
654 : const deallocator *
655 111 : standard_deallocator_set::maybe_get_single () const
656 : {
657 111 : return &m_deallocator;
658 : }
659 :
660 : void
661 362 : standard_deallocator_set::dump_to_pp (pretty_printer *pp) const
662 : {
663 362 : pp_character (pp, '{');
664 362 : pp_string (pp, m_deallocator.m_name);
665 362 : pp_character (pp, '}');
666 362 : }
667 :
668 : /* Return STATE cast to the custom state subclass, or nullptr for the
669 : start state.
670 : Everything should be an allocation_state apart from the start state. */
671 :
672 : static const allocation_state *
673 1733256 : dyn_cast_allocation_state (state_machine::state_t state)
674 : {
675 0 : if (state->get_id () == 0)
676 0 : return nullptr;
677 : return static_cast <const allocation_state *> (state);
678 : }
679 :
680 : /* Return STATE cast to the custom state subclass, for a state that is
681 : already known to not be the start state . */
682 :
683 : static const allocation_state *
684 8832 : as_a_allocation_state (state_machine::state_t state)
685 : {
686 8832 : gcc_assert (state->get_id () != 0);
687 8832 : return static_cast <const allocation_state *> (state);
688 : }
689 :
690 : /* Get the resource_state for STATE. */
691 :
692 : static enum resource_state
693 1733256 : get_rs (state_machine::state_t state)
694 : {
695 0 : if (const allocation_state *astate = dyn_cast_allocation_state (state))
696 208941 : return astate->m_rs;
697 : else
698 : return RS_START;
699 : }
700 :
701 : /* Return true if STATE is the start state. */
702 :
703 : static bool
704 248 : start_p (state_machine::state_t state)
705 : {
706 0 : return get_rs (state) == RS_START;
707 : }
708 :
709 : /* Return true if STATE is an unchecked result from an allocator. */
710 :
711 : static bool
712 39941 : unchecked_p (state_machine::state_t state)
713 : {
714 0 : return get_rs (state) == RS_UNCHECKED;
715 : }
716 :
717 : /* Return true if STATE is a non-null result from an allocator. */
718 :
719 : static bool
720 6449 : nonnull_p (state_machine::state_t state)
721 : {
722 0 : return get_rs (state) == RS_NONNULL;
723 : }
724 :
725 : /* Return true if STATE is a value that has been passed to a deallocator. */
726 :
727 : static bool
728 12408 : freed_p (state_machine::state_t state)
729 : {
730 0 : return get_rs (state) == RS_FREED;
731 : }
732 :
733 : /* Return true if STATE is a value that has been assumed to be non-NULL. */
734 :
735 : static bool
736 207281 : assumed_non_null_p (state_machine::state_t state)
737 : {
738 54967 : return get_rs (state) == RS_ASSUMED_NON_NULL;
739 : }
740 :
741 : /* Class for diagnostics relating to malloc_state_machine. */
742 :
743 0 : class malloc_diagnostic : public pending_diagnostic
744 : {
745 : public:
746 6283 : malloc_diagnostic (const malloc_state_machine &sm, tree arg)
747 6283 : : m_sm (sm), m_arg (arg)
748 : {}
749 :
750 1835 : bool subclass_equal_p (const pending_diagnostic &base_other) const override
751 : {
752 1835 : return same_tree_p (m_arg, ((const malloc_diagnostic &)base_other).m_arg);
753 : }
754 :
755 : bool
756 892 : describe_state_change (pretty_printer &pp,
757 : const evdesc::state_change &change) override
758 : {
759 892 : if (change.m_old_state == m_sm.get_start_state ()
760 892 : && (unchecked_p (change.m_new_state) || nonnull_p (change.m_new_state)))
761 : // TODO: verify that it's the allocation stmt, not a copy
762 : {
763 464 : pp_string (&pp, "allocated here");
764 464 : return true;
765 : }
766 428 : if (unchecked_p (change.m_old_state)
767 304 : && nonnull_p (change.m_new_state))
768 : {
769 252 : if (change.m_expr)
770 236 : pp_printf (&pp, "assuming %qE is non-NULL",
771 : change.m_expr);
772 : else
773 16 : pp_printf (&pp, "assuming %qs is non-NULL",
774 : "<unknown>");
775 252 : return true;
776 : }
777 176 : if (change.m_new_state == m_sm.m_null)
778 : {
779 170 : if (unchecked_p (change.m_old_state))
780 : {
781 48 : if (change.m_expr)
782 48 : pp_printf (&pp, "assuming %qE is NULL",
783 : change.m_expr);
784 : else
785 0 : pp_printf (&pp, "assuming %qs is NULL",
786 : "<unknown>");
787 48 : return true;
788 : }
789 : else
790 : {
791 122 : if (change.m_expr)
792 : {
793 122 : if (zerop (change.m_expr))
794 56 : pp_printf (&pp, "using NULL here");
795 : else
796 66 : pp_printf (&pp, "%qE is NULL",
797 66 : change.m_expr);
798 : }
799 : else
800 0 : pp_printf (&pp, "%qs is NULL",
801 : "<unknown>");
802 122 : return true;
803 : }
804 : }
805 :
806 : return false;
807 : }
808 :
809 : diagnostics::paths::event::meaning
810 204 : get_meaning_for_state_change (const evdesc::state_change &change)
811 : const final override
812 : {
813 204 : if (change.m_old_state == m_sm.get_start_state ()
814 204 : && unchecked_p (change.m_new_state))
815 58 : return diagnostics::paths::event::meaning (diagnostics::paths::event::verb::acquire,
816 58 : diagnostics::paths::event::noun::memory);
817 146 : if (freed_p (change.m_new_state))
818 129 : return diagnostics::paths::event::meaning (diagnostics::paths::event::verb::release,
819 129 : diagnostics::paths::event::noun::memory);
820 17 : return diagnostics::paths::event::meaning ();
821 : }
822 :
823 : protected:
824 : const malloc_state_machine &m_sm;
825 : tree m_arg;
826 : };
827 :
828 : /* Concrete subclass for reporting mismatching allocator/deallocator
829 : diagnostics. */
830 :
831 0 : class mismatching_deallocation : public malloc_diagnostic
832 : {
833 : public:
834 88 : mismatching_deallocation (const malloc_state_machine &sm, tree arg,
835 : const deallocator_set *expected_deallocators,
836 : const deallocator *actual_dealloc)
837 88 : : malloc_diagnostic (sm, arg),
838 88 : m_expected_deallocators (expected_deallocators),
839 88 : m_actual_dealloc (actual_dealloc)
840 : {}
841 :
842 2240 : const char *get_kind () const final override
843 : {
844 2240 : return "mismatching_deallocation";
845 : }
846 :
847 176 : int get_controlling_option () const final override
848 : {
849 176 : return OPT_Wanalyzer_mismatching_deallocation;
850 : }
851 :
852 88 : bool emit (diagnostic_emission_context &ctxt) final override
853 : {
854 88 : auto_diagnostic_group d;
855 88 : ctxt.add_cwe (762); /* CWE-762: Mismatched Memory Management Routines. */
856 176 : if (const deallocator *expected_dealloc
857 88 : = m_expected_deallocators->maybe_get_single ())
858 52 : return ctxt.warn ("%qE should have been deallocated with %qs"
859 : " but was deallocated with %qs",
860 52 : m_arg, expected_dealloc->m_name,
861 52 : m_actual_dealloc->m_name);
862 : else
863 36 : return ctxt.warn ("%qs called on %qE returned from a mismatched"
864 : " allocation function",
865 36 : m_actual_dealloc->m_name, m_arg);
866 88 : }
867 :
868 : bool
869 206 : describe_state_change (pretty_printer &pp,
870 : const evdesc::state_change &change) final override
871 : {
872 206 : if (unchecked_p (change.m_new_state))
873 : {
874 176 : m_alloc_event = change.m_event_id;
875 352 : if (const deallocator *expected_dealloc
876 176 : = m_expected_deallocators->maybe_get_single ())
877 104 : pp_printf (&pp,
878 : "allocated here (expects deallocation with %qs)",
879 104 : expected_dealloc->m_name);
880 : else
881 72 : pp_string (&pp, "allocated here");
882 176 : return true;
883 : }
884 30 : return malloc_diagnostic::describe_state_change (pp, change);
885 : }
886 :
887 : bool
888 176 : describe_final_event (pretty_printer &pp,
889 : const evdesc::final_event &) final override
890 : {
891 176 : if (m_alloc_event.known_p ())
892 : {
893 304 : if (const deallocator *expected_dealloc
894 152 : = m_expected_deallocators->maybe_get_single ())
895 80 : pp_printf (&pp,
896 : "deallocated with %qs here;"
897 : " allocation at %@ expects deallocation with %qs",
898 80 : m_actual_dealloc->m_name, &m_alloc_event,
899 80 : expected_dealloc->m_name);
900 : else
901 72 : pp_printf (&pp,
902 : "deallocated with %qs here;"
903 : " allocated at %@",
904 72 : m_actual_dealloc->m_name, &m_alloc_event);
905 152 : return true;
906 : }
907 24 : pp_printf (&pp, "deallocated with %qs here",
908 24 : m_actual_dealloc->m_name);
909 24 : return true;
910 : }
911 :
912 : private:
913 : diagnostics::paths::event_id_t m_alloc_event;
914 : const deallocator_set *m_expected_deallocators;
915 : const deallocator *m_actual_dealloc;
916 : };
917 :
918 : /* Concrete subclass for reporting double-free diagnostics. */
919 :
920 0 : class double_free : public malloc_diagnostic
921 : {
922 : public:
923 4432 : double_free (const malloc_state_machine &sm, tree arg, const char *funcname)
924 4432 : : malloc_diagnostic (sm, arg), m_funcname (funcname)
925 : {}
926 :
927 4089 : const char *get_kind () const final override { return "double_free"; }
928 :
929 4825 : int get_controlling_option () const final override
930 : {
931 4825 : return OPT_Wanalyzer_double_free;
932 : }
933 :
934 393 : bool emit (diagnostic_emission_context &ctxt) final override
935 : {
936 393 : auto_diagnostic_group d;
937 393 : ctxt.add_cwe (415); /* CWE-415: Double Free. */
938 393 : return ctxt.warn ("double-%qs of %qE", m_funcname, m_arg);
939 393 : }
940 :
941 : bool
942 1080 : describe_state_change (pretty_printer &pp,
943 : const evdesc::state_change &change) final override
944 : {
945 1080 : if (freed_p (change.m_new_state))
946 : {
947 802 : m_first_free_event = change.m_event_id;
948 802 : pp_printf (&pp, "first %qs here", m_funcname);
949 802 : return true;
950 : }
951 278 : return malloc_diagnostic::describe_state_change (pp, change);
952 : }
953 :
954 : bool
955 354 : describe_call_with_state (pretty_printer &pp,
956 : const evdesc::call_with_state &info) final override
957 : {
958 354 : if (freed_p (info.m_state))
959 : {
960 178 : pp_printf (&pp,
961 : "passing freed pointer %qE in call to %qE from %qE",
962 178 : info.m_expr, info.m_callee_fndecl, info.m_caller_fndecl);
963 178 : return true;
964 : }
965 : return false;
966 : }
967 :
968 : bool
969 794 : describe_final_event (pretty_printer &pp,
970 : const evdesc::final_event &) final override
971 : {
972 794 : if (m_first_free_event.known_p ())
973 794 : pp_printf (&pp,
974 : "second %qs here; first %qs was at %@",
975 : m_funcname, m_funcname,
976 : &m_first_free_event);
977 : else
978 0 : pp_printf (&pp, "second %qs here", m_funcname);
979 794 : return true;
980 : }
981 :
982 : private:
983 : diagnostics::paths::event_id_t m_first_free_event;
984 : const char *m_funcname;
985 : };
986 :
987 : /* Abstract subclass for describing possible bad uses of NULL.
988 : Responsible for describing the call that could return NULL. */
989 :
990 0 : class possible_null : public malloc_diagnostic
991 : {
992 : public:
993 332 : possible_null (const malloc_state_machine &sm, tree arg)
994 332 : : malloc_diagnostic (sm, arg)
995 : {}
996 :
997 : bool
998 356 : describe_state_change (pretty_printer &pp,
999 : const evdesc::state_change &change) final override
1000 : {
1001 356 : if (change.m_old_state == m_sm.get_start_state ()
1002 356 : && unchecked_p (change.m_new_state))
1003 : {
1004 356 : m_origin_of_unchecked_event = change.m_event_id;
1005 356 : pp_string (&pp, "this call could return NULL");
1006 356 : return true;
1007 : }
1008 0 : return malloc_diagnostic::describe_state_change (pp, change);
1009 : }
1010 :
1011 : bool
1012 26 : describe_return_of_state (pretty_printer &pp,
1013 : const evdesc::return_of_state &info) final override
1014 : {
1015 26 : if (unchecked_p (info.m_state))
1016 : {
1017 26 : pp_printf (&pp,
1018 : "possible return of NULL to %qE from %qE",
1019 26 : info.m_caller_fndecl, info.m_callee_fndecl);
1020 26 : return true;
1021 : }
1022 : return false;
1023 : }
1024 :
1025 : protected:
1026 : diagnostics::paths::event_id_t m_origin_of_unchecked_event;
1027 : };
1028 :
1029 : /* Concrete subclass for describing dereference of a possible NULL
1030 : value. */
1031 :
1032 0 : class possible_null_deref : public possible_null
1033 : {
1034 : public:
1035 259 : possible_null_deref (const malloc_state_machine &sm, tree arg)
1036 259 : : possible_null (sm, arg)
1037 : {}
1038 :
1039 2697 : const char *get_kind () const final override { return "possible_null_deref"; }
1040 :
1041 288 : int get_controlling_option () const final override
1042 : {
1043 288 : return OPT_Wanalyzer_possible_null_dereference;
1044 : }
1045 :
1046 132 : bool emit (diagnostic_emission_context &ctxt) final override
1047 : {
1048 : /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
1049 132 : ctxt.add_cwe (690);
1050 132 : return ctxt.warn ("dereference of possibly-NULL %qE", m_arg);
1051 : }
1052 :
1053 : bool
1054 264 : describe_final_event (pretty_printer &pp,
1055 : const evdesc::final_event &ev) final override
1056 : {
1057 264 : if (m_origin_of_unchecked_event.known_p ())
1058 264 : pp_printf (&pp,
1059 : "%qE could be NULL: unchecked value from %@",
1060 264 : ev.m_expr,
1061 : &m_origin_of_unchecked_event);
1062 : else
1063 0 : pp_printf (&pp,"%qE could be NULL", ev.m_expr);
1064 264 : return true;
1065 : }
1066 :
1067 : };
1068 :
1069 : /* Return true if FNDECL is a C++ method. */
1070 :
1071 : static bool
1072 657 : method_p (tree fndecl)
1073 : {
1074 657 : return TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE;
1075 : }
1076 :
1077 : /* Return a 1-based description of ARG_IDX (0-based) of FNDECL.
1078 : Compare with %P in the C++ FE (implemented in cp/error.cc: parm_to_string
1079 : as called from cp_printer). */
1080 :
1081 : static label_text
1082 333 : describe_argument_index (tree fndecl, int arg_idx)
1083 : {
1084 333 : if (method_p (fndecl))
1085 27 : if (arg_idx == 0)
1086 9 : return label_text::borrow ("'this'");
1087 324 : pretty_printer pp;
1088 324 : pp_printf (&pp, "%u", arg_idx + 1 - method_p (fndecl));
1089 324 : return label_text::take (xstrdup (pp_formatted_text (&pp)));
1090 324 : }
1091 :
1092 : /* Subroutine for use by possible_null_arg::emit and null_arg::emit.
1093 : Issue a note informing that the pertinent argument must be non-NULL. */
1094 :
1095 : static void
1096 111 : inform_nonnull_attribute (tree fndecl, int arg_idx)
1097 : {
1098 111 : label_text arg_desc = describe_argument_index (fndecl, arg_idx);
1099 111 : inform (DECL_SOURCE_LOCATION (fndecl),
1100 : "argument %s of %qD must be non-null",
1101 : arg_desc.get (), fndecl);
1102 : /* Ideally we would use the location of the parm and underline the
1103 : attribute also - but we don't have the location_t values at this point
1104 : in the middle-end.
1105 : For reference, the C and C++ FEs have get_fndecl_argument_location. */
1106 111 : }
1107 :
1108 : /* Concrete subclass for describing passing a possibly-NULL value to a
1109 : function marked with __attribute__((nonnull)). */
1110 :
1111 0 : class possible_null_arg : public possible_null
1112 : {
1113 : public:
1114 73 : possible_null_arg (const malloc_state_machine &sm, tree arg,
1115 : tree fndecl, int arg_idx)
1116 73 : : possible_null (sm, arg),
1117 73 : m_fndecl (fndecl), m_arg_idx (arg_idx)
1118 : {}
1119 :
1120 1425 : const char *get_kind () const final override { return "possible_null_arg"; }
1121 :
1122 69 : bool subclass_equal_p (const pending_diagnostic &base_other)
1123 : const final override
1124 : {
1125 69 : const possible_null_arg &sub_other
1126 : = (const possible_null_arg &)base_other;
1127 69 : return (same_tree_p (m_arg, sub_other.m_arg)
1128 69 : && m_fndecl == sub_other.m_fndecl
1129 138 : && m_arg_idx == sub_other.m_arg_idx);
1130 : }
1131 :
1132 115 : int get_controlling_option () const final override
1133 : {
1134 115 : return OPT_Wanalyzer_possible_null_argument;
1135 : }
1136 :
1137 42 : bool emit (diagnostic_emission_context &ctxt) final override
1138 : {
1139 : /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
1140 42 : auto_diagnostic_group d;
1141 42 : ctxt.add_cwe (690);
1142 42 : bool warned
1143 42 : = ctxt.warn ("use of possibly-NULL %qE where non-null expected",
1144 : m_arg);
1145 42 : if (warned)
1146 42 : inform_nonnull_attribute (m_fndecl, m_arg_idx);
1147 84 : return warned;
1148 42 : }
1149 :
1150 : bool
1151 84 : describe_final_event (pretty_printer &pp,
1152 : const evdesc::final_event &ev) final override
1153 : {
1154 84 : label_text arg_desc = describe_argument_index (m_fndecl, m_arg_idx);
1155 84 : if (m_origin_of_unchecked_event.known_p ())
1156 84 : pp_printf (&pp,"argument %s (%qE) from %@ could be NULL"
1157 : " where non-null expected",
1158 84 : arg_desc.get (), ev.m_expr,
1159 : &m_origin_of_unchecked_event);
1160 : else
1161 0 : pp_printf (&pp,
1162 : "argument %s (%qE) could be NULL"
1163 : " where non-null expected",
1164 0 : arg_desc.get (), ev.m_expr);
1165 84 : return true;
1166 84 : }
1167 :
1168 : private:
1169 : tree m_fndecl;
1170 : int m_arg_idx;
1171 : };
1172 :
1173 : /* Concrete subclass for describing a dereference of a NULL value. */
1174 :
1175 0 : class null_deref : public malloc_diagnostic
1176 : {
1177 : public:
1178 192 : null_deref (const malloc_state_machine &sm, tree arg)
1179 192 : : malloc_diagnostic (sm, arg) {}
1180 :
1181 2283 : const char *get_kind () const final override { return "null_deref"; }
1182 :
1183 306 : int get_controlling_option () const final override
1184 : {
1185 306 : return OPT_Wanalyzer_null_dereference;
1186 : }
1187 :
1188 180 : bool terminate_path_p () const final override { return true; }
1189 :
1190 126 : bool emit (diagnostic_emission_context &ctxt) final override
1191 : {
1192 : /* CWE-476: NULL Pointer Dereference. */
1193 126 : ctxt.add_cwe (476);
1194 126 : return ctxt.warn ("dereference of NULL %qE", m_arg);
1195 : }
1196 :
1197 : bool
1198 22 : describe_return_of_state (pretty_printer &pp,
1199 : const evdesc::return_of_state &info)
1200 : final override
1201 : {
1202 22 : if (info.m_state == m_sm.m_null)
1203 : {
1204 22 : pp_printf (&pp, "return of NULL to %qE from %qE",
1205 22 : info.m_caller_fndecl, info.m_callee_fndecl);
1206 22 : return true;
1207 : }
1208 : return false;
1209 : }
1210 :
1211 : bool
1212 252 : describe_final_event (pretty_printer &pp,
1213 : const evdesc::final_event &ev) final override
1214 : {
1215 252 : pp_printf (&pp, "dereference of NULL %qE", ev.m_expr);
1216 252 : return true;
1217 : }
1218 :
1219 : /* Implementation of pending_diagnostic::supercedes_p for
1220 : null-deref.
1221 :
1222 : We want null-deref to supercede use-of-unitialized-value,
1223 : so that if we have these at the same stmt, we don't emit
1224 : a use-of-uninitialized, just the null-deref. */
1225 :
1226 155 : bool supercedes_p (const pending_diagnostic &other) const final override
1227 : {
1228 155 : if (other.use_of_uninit_p ())
1229 : return true;
1230 :
1231 : return false;
1232 : }
1233 : };
1234 :
1235 : /* Concrete subclass for describing passing a NULL value to a
1236 : function marked with __attribute__((nonnull)). */
1237 :
1238 0 : class null_arg : public malloc_diagnostic
1239 : {
1240 : public:
1241 72 : null_arg (const malloc_state_machine &sm, tree arg,
1242 : tree fndecl, int arg_idx)
1243 72 : : malloc_diagnostic (sm, arg),
1244 72 : m_fndecl (fndecl), m_arg_idx (arg_idx)
1245 : {}
1246 :
1247 489 : const char *get_kind () const final override { return "null_arg"; }
1248 :
1249 71 : bool subclass_equal_p (const pending_diagnostic &base_other)
1250 : const final override
1251 : {
1252 71 : const null_arg &sub_other
1253 : = (const null_arg &)base_other;
1254 71 : return (same_tree_p (m_arg, sub_other.m_arg)
1255 71 : && m_fndecl == sub_other.m_fndecl
1256 142 : && m_arg_idx == sub_other.m_arg_idx);
1257 : }
1258 :
1259 141 : int get_controlling_option () const final override
1260 : {
1261 141 : return OPT_Wanalyzer_null_argument;
1262 : }
1263 :
1264 72 : bool terminate_path_p () const final override { return true; }
1265 :
1266 69 : bool emit (diagnostic_emission_context &ctxt) final override
1267 : {
1268 : /* CWE-476: NULL Pointer Dereference. */
1269 69 : auto_diagnostic_group d;
1270 69 : ctxt.add_cwe (476);
1271 :
1272 69 : bool warned;
1273 69 : if (zerop (m_arg))
1274 64 : warned = ctxt.warn ("use of NULL where non-null expected");
1275 : else
1276 5 : warned = ctxt.warn ("use of NULL %qE where non-null expected",
1277 : m_arg);
1278 69 : if (warned)
1279 69 : inform_nonnull_attribute (m_fndecl, m_arg_idx);
1280 138 : return warned;
1281 69 : }
1282 :
1283 : bool
1284 138 : describe_final_event (pretty_printer &pp,
1285 : const evdesc::final_event &ev) final override
1286 : {
1287 138 : label_text arg_desc = describe_argument_index (m_fndecl, m_arg_idx);
1288 138 : if (zerop (ev.m_expr))
1289 112 : pp_printf (&pp,
1290 : "argument %s NULL where non-null expected",
1291 : arg_desc.get ());
1292 : else
1293 26 : pp_printf (&pp,
1294 : "argument %s (%qE) NULL"
1295 : " where non-null expected",
1296 26 : arg_desc.get (), ev.m_expr);
1297 138 : return true;
1298 138 : }
1299 :
1300 : private:
1301 : tree m_fndecl;
1302 : int m_arg_idx;
1303 : };
1304 :
1305 0 : class use_after_free : public malloc_diagnostic
1306 : {
1307 : public:
1308 84 : use_after_free (const malloc_state_machine &sm, tree arg,
1309 : const deallocator *deallocator)
1310 84 : : malloc_diagnostic (sm, arg),
1311 84 : m_deallocator (deallocator)
1312 : {
1313 84 : gcc_assert (deallocator);
1314 84 : }
1315 :
1316 1501 : const char *get_kind () const final override { return "use_after_free"; }
1317 :
1318 161 : int get_controlling_option () const final override
1319 : {
1320 161 : return OPT_Wanalyzer_use_after_free;
1321 : }
1322 :
1323 77 : bool emit (diagnostic_emission_context &ctxt) final override
1324 : {
1325 : /* CWE-416: Use After Free. */
1326 77 : ctxt.add_cwe (416);
1327 77 : return ctxt.warn ("use after %qs of %qE",
1328 77 : m_deallocator->m_name, m_arg);
1329 : }
1330 :
1331 : bool
1332 348 : describe_state_change (pretty_printer &pp,
1333 : const evdesc::state_change &change) final override
1334 : {
1335 348 : if (freed_p (change.m_new_state))
1336 : {
1337 156 : m_free_event = change.m_event_id;
1338 156 : switch (m_deallocator->m_wording)
1339 : {
1340 0 : default:
1341 0 : case WORDING_REALLOCATED:
1342 0 : gcc_unreachable ();
1343 124 : case WORDING_FREED:
1344 124 : pp_string (&pp, "freed here");
1345 124 : return true;
1346 30 : case WORDING_DELETED:
1347 30 : pp_string (&pp, "deleted here");
1348 30 : return true;
1349 2 : case WORDING_DEALLOCATED:
1350 2 : pp_string (&pp, "deallocated here");
1351 2 : return true;
1352 : }
1353 : }
1354 192 : return malloc_diagnostic::describe_state_change (pp, change);
1355 : }
1356 :
1357 : bool
1358 156 : describe_final_event (pretty_printer &pp,
1359 : const evdesc::final_event &ev) final override
1360 : {
1361 156 : const char *funcname = m_deallocator->m_name;
1362 156 : if (m_free_event.known_p ())
1363 156 : switch (m_deallocator->m_wording)
1364 : {
1365 0 : default:
1366 0 : case WORDING_REALLOCATED:
1367 0 : gcc_unreachable ();
1368 124 : case WORDING_FREED:
1369 124 : pp_printf (&pp,
1370 : "use after %qs of %qE; freed at %@",
1371 124 : funcname, ev.m_expr, &m_free_event);
1372 124 : return true;
1373 30 : case WORDING_DELETED:
1374 30 : pp_printf (&pp,
1375 : "use after %qs of %qE; deleted at %@",
1376 30 : funcname, ev.m_expr, &m_free_event);
1377 30 : return true;
1378 2 : case WORDING_DEALLOCATED:
1379 2 : pp_printf (&pp,
1380 : "use after %qs of %qE;"
1381 : " deallocated at %@",
1382 2 : funcname, ev.m_expr, &m_free_event);
1383 2 : return true;
1384 : }
1385 : else
1386 : {
1387 0 : pp_printf (&pp,
1388 : "use after %qs of %qE",
1389 0 : funcname, ev.m_expr);
1390 0 : return true;
1391 : }
1392 : }
1393 :
1394 : /* Implementation of pending_diagnostic::supercedes_p for
1395 : use_after_free.
1396 :
1397 : We want use-after-free to supercede use-of-unitialized-value,
1398 : so that if we have these at the same stmt, we don't emit
1399 : a use-of-uninitialized, just the use-after-free.
1400 : (this is because we fully purge information about freed
1401 : buffers when we free them to avoid state explosions, so
1402 : that if they are accessed after the free, it looks like
1403 : they are uninitialized). */
1404 :
1405 129 : bool supercedes_p (const pending_diagnostic &other) const final override
1406 : {
1407 129 : if (other.use_of_uninit_p ())
1408 : return true;
1409 :
1410 : return false;
1411 : }
1412 :
1413 : private:
1414 : diagnostics::paths::event_id_t m_free_event;
1415 : const deallocator *m_deallocator;
1416 : };
1417 :
1418 : class malloc_leak : public malloc_diagnostic
1419 : {
1420 : public:
1421 883 : malloc_leak (const malloc_state_machine &sm, tree arg,
1422 : const program_state *final_state)
1423 883 : : malloc_diagnostic (sm, arg),
1424 883 : m_final_state ()
1425 : {
1426 883 : if (final_state)
1427 883 : m_final_state = std::make_unique<program_state> (*final_state);
1428 883 : }
1429 :
1430 7377 : const char *get_kind () const final override { return "malloc_leak"; }
1431 :
1432 1249 : int get_controlling_option () const final override
1433 : {
1434 1249 : return OPT_Wanalyzer_malloc_leak;
1435 : }
1436 :
1437 366 : bool emit (diagnostic_emission_context &ctxt) final override
1438 : {
1439 : /* "CWE-401: Missing Release of Memory after Effective Lifetime". */
1440 366 : ctxt.add_cwe (401);
1441 366 : if (m_arg)
1442 309 : return ctxt.warn ("leak of %qE", m_arg);
1443 : else
1444 57 : return ctxt.warn ("leak of %qs", "<unknown>");
1445 : }
1446 :
1447 : bool
1448 828 : describe_state_change (pretty_printer &pp,
1449 : const evdesc::state_change &change) final override
1450 : {
1451 828 : if (unchecked_p (change.m_new_state)
1452 828 : || (start_p (change.m_old_state) && nonnull_p (change.m_new_state)))
1453 : {
1454 666 : m_alloc_event = change.m_event_id;
1455 666 : pp_string (&pp, "allocated here");
1456 666 : return true;
1457 : }
1458 162 : return malloc_diagnostic::describe_state_change (pp, change);
1459 : }
1460 :
1461 : bool
1462 728 : describe_final_event (pretty_printer &pp,
1463 : const evdesc::final_event &ev) final override
1464 : {
1465 728 : if (ev.m_expr)
1466 : {
1467 614 : if (m_alloc_event.known_p ())
1468 614 : pp_printf (&pp,
1469 : "%qE leaks here; was allocated at %@",
1470 : ev.m_expr, &m_alloc_event);
1471 : else
1472 0 : pp_printf (&pp,
1473 : "%qE leaks here", ev.m_expr);
1474 : }
1475 : else
1476 : {
1477 114 : if (m_alloc_event.known_p ())
1478 52 : pp_printf (&pp,
1479 : "%qs leaks here; was allocated at %@",
1480 : "<unknown>", &m_alloc_event);
1481 : else
1482 62 : pp_printf (&pp,
1483 : "%qs leaks here", "<unknown>");
1484 : }
1485 728 : return true;
1486 : }
1487 :
1488 : const program_state *
1489 366 : get_final_state () const final override
1490 : {
1491 366 : return m_final_state.get ();
1492 : }
1493 :
1494 : private:
1495 : diagnostics::paths::event_id_t m_alloc_event;
1496 : std::unique_ptr<program_state> m_final_state;
1497 : };
1498 :
1499 0 : class free_of_non_heap : public malloc_diagnostic
1500 : {
1501 : public:
1502 61 : free_of_non_heap (const malloc_state_machine &sm, tree arg,
1503 : const region *freed_reg,
1504 : const char *funcname)
1505 61 : : malloc_diagnostic (sm, arg), m_freed_reg (freed_reg), m_funcname (funcname)
1506 : {
1507 : }
1508 :
1509 1240 : const char *get_kind () const final override { return "free_of_non_heap"; }
1510 :
1511 41 : bool subclass_equal_p (const pending_diagnostic &base_other) const
1512 : final override
1513 : {
1514 41 : const free_of_non_heap &other = (const free_of_non_heap &)base_other;
1515 41 : return (same_tree_p (m_arg, other.m_arg)
1516 41 : && m_freed_reg == other.m_freed_reg);
1517 : }
1518 :
1519 102 : int get_controlling_option () const final override
1520 : {
1521 102 : return OPT_Wanalyzer_free_of_non_heap;
1522 : }
1523 :
1524 41 : bool emit (diagnostic_emission_context &ctxt) final override
1525 : {
1526 41 : auto_diagnostic_group d;
1527 41 : ctxt.add_cwe (590); /* CWE-590: Free of Memory not on the Heap. */
1528 41 : switch (get_memory_space ())
1529 : {
1530 0 : default:
1531 0 : case MEMSPACE_HEAP:
1532 0 : gcc_unreachable ();
1533 10 : case MEMSPACE_UNKNOWN:
1534 10 : case MEMSPACE_CODE:
1535 10 : case MEMSPACE_GLOBALS:
1536 10 : case MEMSPACE_READONLY_DATA:
1537 10 : return ctxt.warn ("%qs of %qE which points to memory"
1538 : " not on the heap",
1539 10 : m_funcname, m_arg);
1540 31 : break;
1541 31 : case MEMSPACE_STACK:
1542 31 : return ctxt.warn ("%qs of %qE which points to memory"
1543 : " on the stack",
1544 31 : m_funcname, m_arg);
1545 41 : break;
1546 : }
1547 41 : }
1548 :
1549 : bool
1550 0 : describe_state_change (pretty_printer &pp,
1551 : const evdesc::state_change &) final override
1552 : {
1553 0 : pp_string (&pp, "pointer is from here");
1554 0 : return true;
1555 : }
1556 :
1557 : bool
1558 82 : describe_final_event (pretty_printer &pp,
1559 : const evdesc::final_event &) final override
1560 : {
1561 82 : pp_printf (&pp, "call to %qs here", m_funcname);
1562 82 : return true;
1563 : }
1564 :
1565 41 : void mark_interesting_stuff (interesting_t *interest) final override
1566 : {
1567 41 : if (m_freed_reg)
1568 41 : interest->add_region_creation (m_freed_reg);
1569 41 : }
1570 :
1571 : private:
1572 41 : enum memory_space get_memory_space () const
1573 : {
1574 41 : if (m_freed_reg)
1575 41 : return m_freed_reg->get_memory_space ();
1576 : else
1577 : return MEMSPACE_UNKNOWN;
1578 : }
1579 :
1580 : const region *m_freed_reg;
1581 : const char *m_funcname;
1582 : };
1583 :
1584 : /* Concrete pending_diagnostic subclass for -Wanalyzer-deref-before-check. */
1585 :
1586 0 : class deref_before_check : public malloc_diagnostic
1587 : {
1588 : public:
1589 139 : deref_before_check (const malloc_state_machine &sm, tree arg)
1590 139 : : malloc_diagnostic (sm, arg),
1591 139 : m_deref_enode (nullptr),
1592 139 : m_deref_expr (nullptr),
1593 139 : m_check_enode (nullptr)
1594 : {
1595 139 : gcc_assert (arg);
1596 139 : }
1597 :
1598 1370 : const char *get_kind () const final override { return "deref_before_check"; }
1599 :
1600 189 : int get_controlling_option () const final override
1601 : {
1602 189 : return OPT_Wanalyzer_deref_before_check;
1603 : }
1604 :
1605 114 : bool emit (diagnostic_emission_context &ctxt) final override
1606 : {
1607 114 : LOG_SCOPE (ctxt.get_logger ());
1608 114 : logger *logger = ctxt.get_logger ();
1609 :
1610 : /* Don't emit the warning if we can't show where the deref
1611 : and the check occur. */
1612 114 : if (!m_deref_enode)
1613 : {
1614 4 : if (logger)
1615 0 : logger->log ("rejecting: no deref enode");
1616 4 : return false;
1617 : }
1618 110 : if (!m_check_enode)
1619 : {
1620 0 : if (logger)
1621 0 : logger->log ("rejecting: no check enode");
1622 0 : return false;
1623 : }
1624 : /* Only emit the warning for intraprocedural cases. */
1625 110 : const program_point &deref_point = m_deref_enode->get_point ();
1626 110 : const program_point &check_point = m_check_enode->get_point ();
1627 :
1628 110 : if (!program_point::effectively_intraprocedural_p (deref_point,
1629 : check_point))
1630 : {
1631 16 : if (logger)
1632 0 : logger->log ("rejecting: not effectively intraprocedural");
1633 16 : return false;
1634 : }
1635 :
1636 : /* Reject the warning if the check occurs within a macro defintion.
1637 : This avoids false positives for such code as:
1638 :
1639 : #define throw_error \
1640 : do { \
1641 : if (p) \
1642 : cleanup (p); \
1643 : return; \
1644 : } while (0)
1645 :
1646 : if (p->idx >= n)
1647 : throw_error ();
1648 :
1649 : where the usage of "throw_error" implicitly adds a check
1650 : on 'p'.
1651 :
1652 : We do warn when the check is in a macro expansion if we can get
1653 : at the location of the condition and it is't part of the
1654 : definition, so that we warn for checks such as:
1655 : if (words[0][0] == '@')
1656 : return;
1657 : g_assert(words[0] != NULL); <--- here
1658 : Unfortunately we don't have locations for individual gimple
1659 : arguments, so in:
1660 : g_assert (ptr);
1661 : we merely have a gimple_cond
1662 : if (p_2(D) == 0B)
1663 : with no way of getting at the location of the condition separately
1664 : from that of the gimple_cond (where the "if" is within the macro
1665 : definition). We reject the warning for such cases.
1666 :
1667 : We do warn when the *deref* occurs in a macro, since this can be
1668 : a source of real bugs; see e.g. PR 77425. */
1669 94 : location_t check_loc = m_check_enode->get_point ().get_location ();
1670 94 : if (linemap_location_from_macro_definition_p (line_table, check_loc))
1671 : {
1672 10 : if (logger)
1673 0 : logger->log ("rejecting: check occurs within macro definition");
1674 10 : return false;
1675 : }
1676 :
1677 : /* Reject warning if the check is in a loop header within a
1678 : macro expansion. This rejects cases like:
1679 : | deref of x;
1680 : | [...snip...]
1681 : | FOR_EACH(x) {
1682 : | [...snip...]
1683 : | }
1684 : where the FOR_EACH macro tests for non-nullness of x, since
1685 : the user is hoping to encapsulate the details of iteration
1686 : in the macro, and the extra check on the first iteration
1687 : would just be noise if we reported it. */
1688 84 : if (loop_header_p (m_check_enode->get_point ())
1689 84 : && linemap_location_from_macro_expansion_p (line_table, check_loc))
1690 : {
1691 4 : if (logger)
1692 0 : logger->log
1693 0 : ("rejecting: check occurs in loop header macro expansion");
1694 4 : return false;
1695 : }
1696 :
1697 : /* Reject if m_deref_expr is sufficiently different from m_arg
1698 : for cases where the dereference is spelled differently from
1699 : the check, which is probably two different ways to get the
1700 : same svalue, and thus not worth reporting. */
1701 80 : if (!m_deref_expr)
1702 : {
1703 18 : if (logger)
1704 0 : logger->log ("rejecting: no deref_expr");
1705 18 : return false;
1706 : }
1707 62 : if (!sufficiently_similar_p (m_deref_expr, m_arg))
1708 : {
1709 4 : if (logger)
1710 0 : logger->log ("rejecting: not sufficiently similar to arg");
1711 4 : return false;
1712 : }
1713 :
1714 : /* Reject the warning if the deref's BB doesn't dominate that
1715 : of the check, so that we don't warn e.g. for shared cleanup
1716 : code that checks a pointer for NULL, when that code is sometimes
1717 : used before a deref and sometimes after.
1718 : Using the dominance code requires setting cfun. */
1719 116 : auto_cfun sentinel (m_deref_enode->get_function ());
1720 58 : calculate_dominance_info (CDI_DOMINATORS);
1721 58 : if (!dominated_by_p (CDI_DOMINATORS,
1722 58 : m_check_enode->get_supernode ()->m_bb,
1723 58 : m_deref_enode->get_supernode ()->m_bb))
1724 : {
1725 8 : if (logger)
1726 0 : logger->log ("rejecting: deref doesn't dominate the check");
1727 8 : return false;
1728 : }
1729 :
1730 50 : return ctxt.warn ("check of %qE for NULL after already"
1731 : " dereferencing it",
1732 50 : m_arg);
1733 114 : }
1734 :
1735 : bool
1736 172 : describe_state_change (pretty_printer &pp,
1737 : const evdesc::state_change &change) final override
1738 : {
1739 172 : if (change.m_old_state == m_sm.get_start_state ()
1740 172 : && assumed_non_null_p (change.m_new_state))
1741 : {
1742 160 : m_first_deref_event = change.m_event_id;
1743 160 : m_deref_enode = change.m_event.get_exploded_node ();
1744 160 : m_deref_expr = change.m_expr;
1745 160 : pp_printf (&pp,
1746 : "pointer %qE is dereferenced here",
1747 : m_arg);
1748 160 : return true;
1749 : }
1750 12 : return malloc_diagnostic::describe_state_change (pp, change);
1751 : }
1752 :
1753 : bool
1754 164 : describe_final_event (pretty_printer &pp,
1755 : const evdesc::final_event &ev) final override
1756 : {
1757 164 : m_check_enode = ev.m_event.get_exploded_node ();
1758 164 : if (m_first_deref_event.known_p ())
1759 160 : pp_printf (&pp,
1760 : "pointer %qE is checked for NULL here but"
1761 : " it was already dereferenced at %@",
1762 : m_arg, &m_first_deref_event);
1763 : else
1764 4 : pp_printf (&pp,
1765 : "pointer %qE is checked for NULL here but"
1766 : " it was already dereferenced",
1767 : m_arg);
1768 164 : return true;
1769 : }
1770 :
1771 : private:
1772 84 : static bool loop_header_p (const program_point &point)
1773 : {
1774 84 : const supernode *snode = point.get_supernode ();
1775 84 : if (!snode)
1776 : return false;
1777 365 : for (auto in_edge : snode->m_bb->preds)
1778 117 : if (in_edge->flags & EDGE_DFS_BACK)
1779 : return true;
1780 : return false;
1781 : }
1782 :
1783 62 : static bool sufficiently_similar_p (tree expr_a, tree expr_b)
1784 : {
1785 62 : auto pp_a = global_dc->clone_printer ();
1786 62 : auto pp_b = global_dc->clone_printer ();
1787 62 : pp_printf (pp_a.get (), "%qE", expr_a);
1788 62 : pp_printf (pp_b.get (), "%qE", expr_b);
1789 62 : bool result = (strcmp (pp_formatted_text (pp_a.get ()),
1790 : pp_formatted_text (pp_b.get ()))
1791 62 : == 0);
1792 124 : return result;
1793 62 : }
1794 :
1795 : diagnostics::paths::event_id_t m_first_deref_event;
1796 : const exploded_node *m_deref_enode;
1797 : tree m_deref_expr;
1798 : const exploded_node *m_check_enode;
1799 : };
1800 :
1801 : /* struct allocation_state : public state_machine::state. */
1802 :
1803 : /* Implementation of state_machine::state::dump_to_pp vfunc
1804 : for allocation_state: append the API that this allocation is
1805 : associated with. */
1806 :
1807 : void
1808 703 : allocation_state::dump_to_pp (pretty_printer *pp) const
1809 : {
1810 703 : state_machine::state::dump_to_pp (pp);
1811 703 : if (m_deallocators)
1812 : {
1813 320 : pp_string (pp, " (");
1814 320 : m_deallocators->dump_to_pp (pp);
1815 320 : pp_character (pp, ')');
1816 : }
1817 703 : }
1818 :
1819 : /* Given a allocation_state for a deallocator_set, get the "nonnull" state
1820 : for the corresponding allocator(s). */
1821 :
1822 : const allocation_state *
1823 1543 : allocation_state::get_nonnull () const
1824 : {
1825 1543 : gcc_assert (m_deallocators);
1826 1543 : return as_a_allocation_state (m_deallocators->m_nonnull);
1827 : }
1828 :
1829 : /* struct assumed_non_null_state : public allocation_state. */
1830 :
1831 : void
1832 367 : assumed_non_null_state::dump_to_pp (pretty_printer *pp) const
1833 : {
1834 367 : allocation_state::dump_to_pp (pp);
1835 367 : pp_string (pp, " (in ");
1836 367 : m_frame->dump_to_pp (pp, true);
1837 367 : pp_character (pp, ')');
1838 367 : }
1839 :
1840 : /* malloc_state_machine's ctor. */
1841 :
1842 3389 : malloc_state_machine::malloc_state_machine (logger *logger)
1843 : : state_machine ("malloc", logger),
1844 3389 : m_free (this, "free", WORDING_FREED),
1845 3389 : m_scalar_delete (this, "delete", WORDING_DELETED),
1846 3389 : m_vector_delete (this, "delete[]", WORDING_DELETED),
1847 6778 : m_realloc (this, "realloc", WORDING_REALLOCATED)
1848 : {
1849 3389 : gcc_assert (m_start->get_id () == 0);
1850 3389 : m_null = add_state ("null", RS_FREED, nullptr, nullptr);
1851 3389 : m_non_heap = add_state ("non-heap", RS_NON_HEAP, nullptr, nullptr);
1852 3389 : m_stop = add_state ("stop", RS_STOP, nullptr, nullptr);
1853 3389 : }
1854 :
1855 6778 : malloc_state_machine::~malloc_state_machine ()
1856 : {
1857 3389 : unsigned i;
1858 3389 : custom_deallocator_set *set;
1859 3464 : FOR_EACH_VEC_ELT (m_dynamic_sets, i, set)
1860 75 : delete set;
1861 : custom_deallocator *d;
1862 3459 : FOR_EACH_VEC_ELT (m_dynamic_deallocators, i, d)
1863 70 : delete d;
1864 6778 : }
1865 :
1866 : state_machine::state_t
1867 44277 : malloc_state_machine::add_state (const char *name, enum resource_state rs,
1868 : const deallocator_set *deallocators,
1869 : const deallocator *deallocator)
1870 : {
1871 88554 : return add_custom_state (new allocation_state (name, alloc_state_id (),
1872 : rs, deallocators,
1873 44277 : deallocator));
1874 : }
1875 :
1876 : /* If ALLOCATOR_FNDECL has any "__attribute__((malloc(FOO)))",
1877 : return a custom_deallocator_set for them, consolidating them
1878 : to ensure uniqueness of the sets.
1879 :
1880 : Return nullptr if it has no such attributes. */
1881 :
1882 : const custom_deallocator_set *
1883 27735 : malloc_state_machine::
1884 : get_or_create_custom_deallocator_set (tree allocator_fndecl)
1885 : {
1886 : /* Early rejection of decls without attributes. */
1887 27735 : tree attrs = DECL_ATTRIBUTES (allocator_fndecl);
1888 27735 : if (!attrs)
1889 : return nullptr;
1890 :
1891 : /* Otherwise, call maybe_create_custom_deallocator_set,
1892 : memoizing the result. */
1893 26252 : if (custom_deallocator_set **slot
1894 13126 : = m_custom_deallocator_set_cache.get (allocator_fndecl))
1895 10420 : return *slot;
1896 2706 : custom_deallocator_set *set
1897 2706 : = maybe_create_custom_deallocator_set (allocator_fndecl);
1898 2706 : m_custom_deallocator_set_cache.put (allocator_fndecl, set);
1899 2706 : return set;
1900 : }
1901 :
1902 : /* Given ALLOCATOR_FNDECL, a FUNCTION_DECL with attributes,
1903 : look for any "__attribute__((malloc(FOO)))" and return a
1904 : custom_deallocator_set for them, consolidating them
1905 : to ensure uniqueness of the sets.
1906 :
1907 : Return nullptr if it has no such attributes.
1908 :
1909 : Subroutine of get_or_create_custom_deallocator_set which
1910 : memoizes the result. */
1911 :
1912 : custom_deallocator_set *
1913 2706 : malloc_state_machine::
1914 : maybe_create_custom_deallocator_set (tree allocator_fndecl)
1915 : {
1916 2706 : tree attrs = DECL_ATTRIBUTES (allocator_fndecl);
1917 2706 : gcc_assert (attrs);
1918 :
1919 : /* Look for instances of __attribute__((malloc(FOO))). */
1920 2706 : auto_vec<const deallocator *> deallocator_vec;
1921 2706 : for (tree allocs = attrs;
1922 2928 : (allocs = lookup_attribute ("malloc", allocs));
1923 222 : allocs = TREE_CHAIN (allocs))
1924 : {
1925 222 : tree args = TREE_VALUE (allocs);
1926 222 : if (!args)
1927 114 : continue;
1928 108 : if (TREE_VALUE (args))
1929 : {
1930 108 : const deallocator *d
1931 108 : = get_or_create_deallocator (TREE_VALUE (args));
1932 108 : deallocator_vec.safe_push (d);
1933 : }
1934 : }
1935 :
1936 : /* If there weren't any deallocators, bail. */
1937 2794 : if (deallocator_vec.length () == 0)
1938 : return nullptr;
1939 :
1940 : /* Consolidate, so that we reuse existing deallocator_set
1941 : instances. */
1942 88 : deallocator_vec.qsort (deallocator::cmp_ptr_ptr);
1943 88 : custom_deallocator_set **slot
1944 88 : = m_custom_deallocator_set_map.get (&deallocator_vec);
1945 88 : if (slot)
1946 13 : return *slot;
1947 75 : custom_deallocator_set *set
1948 75 : = new custom_deallocator_set (this, &deallocator_vec, WORDING_DEALLOCATED);
1949 75 : m_custom_deallocator_set_map.put (&set->m_deallocator_vec, set);
1950 75 : m_dynamic_sets.safe_push (set);
1951 75 : return set;
1952 2706 : }
1953 :
1954 : /* Get the deallocator for DEALLOCATOR_FNDECL, creating it if necessary. */
1955 :
1956 : const deallocator *
1957 299 : malloc_state_machine::get_or_create_deallocator (tree deallocator_fndecl)
1958 : {
1959 299 : deallocator **slot = m_deallocator_map.get (deallocator_fndecl);
1960 299 : if (slot)
1961 219 : return *slot;
1962 :
1963 : /* Reuse "free". */
1964 80 : deallocator *d;
1965 80 : if (is_named_call_p (deallocator_fndecl, "free")
1966 71 : || is_std_named_call_p (deallocator_fndecl, "free")
1967 151 : || is_named_call_p (deallocator_fndecl, "__builtin_free"))
1968 10 : d = &m_free.m_deallocator;
1969 : else
1970 : {
1971 70 : custom_deallocator *cd
1972 : = new custom_deallocator (this, deallocator_fndecl,
1973 70 : WORDING_DEALLOCATED);
1974 70 : m_dynamic_deallocators.safe_push (cd);
1975 70 : d = cd;
1976 : }
1977 80 : m_deallocator_map.put (deallocator_fndecl, d);
1978 80 : return d;
1979 : }
1980 :
1981 : /* Get the "assumed-non-null" state for assumptions made within FRAME,
1982 : creating it if necessary. */
1983 :
1984 : state_machine::state_t
1985 9723 : malloc_state_machine::
1986 : get_or_create_assumed_non_null_state_for_frame (const frame_region *frame)
1987 : {
1988 9723 : if (state_t *slot = m_assumed_non_null.get (frame))
1989 7186 : return *slot;
1990 2537 : state_machine::state *new_state
1991 2537 : = new assumed_non_null_state ("assumed-non-null", alloc_state_id (), frame);
1992 2537 : add_custom_state (new_state);
1993 2537 : m_assumed_non_null.put (frame, new_state);
1994 2537 : return new_state;
1995 : }
1996 :
1997 : /* Try to identify the function declaration either by name or as a known malloc
1998 : builtin. */
1999 :
2000 : static bool
2001 45860 : known_allocator_p (const_tree fndecl, const gcall &call)
2002 : {
2003 : /* Either it is a function we know by name and number of arguments... */
2004 45860 : if (is_named_call_p (fndecl, "malloc", call, 1)
2005 44217 : || is_named_call_p (fndecl, "calloc", call, 2)
2006 44119 : || is_std_named_call_p (fndecl, "malloc", call, 1)
2007 44116 : || is_std_named_call_p (fndecl, "calloc", call, 2)
2008 44113 : || is_named_call_p (fndecl, "strdup", call, 1)
2009 89787 : || is_named_call_p (fndecl, "strndup", call, 2))
2010 1947 : return true;
2011 :
2012 : /* ... or it is a builtin allocator that allocates objects freed with
2013 : __builtin_free. */
2014 43913 : if (fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
2015 21397 : switch (DECL_FUNCTION_CODE (fndecl))
2016 : {
2017 : case BUILT_IN_MALLOC:
2018 : case BUILT_IN_CALLOC:
2019 : case BUILT_IN_STRDUP:
2020 : case BUILT_IN_STRNDUP:
2021 : return true;
2022 : default:
2023 : break;
2024 : }
2025 :
2026 : return false;
2027 : }
2028 :
2029 : /* If PTR's nullness is not known, transition it to the "assumed-non-null"
2030 : state for the current frame. */
2031 :
2032 : void
2033 25631 : malloc_state_machine::maybe_assume_non_null (sm_context &sm_ctxt,
2034 : tree ptr) const
2035 : {
2036 25631 : const region_model *old_model = sm_ctxt.get_old_region_model ();
2037 25631 : if (!old_model)
2038 0 : return;
2039 :
2040 25631 : tree null_ptr_cst = build_int_cst (TREE_TYPE (ptr), 0);
2041 25631 : tristate known_non_null
2042 25631 : = old_model->eval_condition (ptr, NE_EXPR, null_ptr_cst, nullptr);
2043 25631 : if (known_non_null.is_unknown ())
2044 : {
2045 : /* Cast away const-ness for cache-like operations. */
2046 9723 : malloc_state_machine *mut_this
2047 : = const_cast <malloc_state_machine *> (this);
2048 9723 : state_t next_state
2049 : = mut_this->get_or_create_assumed_non_null_state_for_frame
2050 9723 : (old_model->get_current_frame ());
2051 9723 : sm_ctxt.set_next_state (ptr, next_state);
2052 : }
2053 : }
2054 :
2055 : /* Helper method for malloc_state_machine::on_stmt. Handle a single
2056 : argument (Ith argument ARG) if it is nonnull or nonnull_if_nonzero
2057 : and size is nonzero. */
2058 :
2059 : void
2060 7434 : malloc_state_machine::handle_nonnull (sm_context &sm_ctxt,
2061 : tree fndecl,
2062 : tree arg,
2063 : unsigned i) const
2064 : {
2065 7434 : state_t state = sm_ctxt.get_state (arg);
2066 : /* Can't use a switch as the states are non-const. */
2067 : /* Do use the fndecl that caused the warning so that the
2068 : misused attributes are printed and the user not confused. */
2069 7434 : if (unchecked_p (state))
2070 : {
2071 73 : tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
2072 73 : sm_ctxt.warn
2073 73 : (arg,
2074 73 : std::make_unique<possible_null_arg> (*this, diag_arg, fndecl,
2075 : i));
2076 73 : const allocation_state *astate
2077 73 : = as_a_allocation_state (state);
2078 73 : sm_ctxt.set_next_state (arg, astate->get_nonnull ());
2079 : }
2080 7361 : else if (state == m_null)
2081 : {
2082 72 : tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
2083 72 : sm_ctxt.warn (arg,
2084 72 : std::make_unique<null_arg> (*this, diag_arg, fndecl, i));
2085 72 : sm_ctxt.set_next_state (arg, m_stop);
2086 : }
2087 7289 : else if (state == m_start)
2088 3246 : maybe_assume_non_null (sm_ctxt, arg);
2089 7434 : }
2090 :
2091 : /* Return true if it's valid to dereference the zero value of PTR_TYPE,
2092 : or false if we should warn on it. */
2093 :
2094 : static bool
2095 453 : zero_address_valid_p (const_tree ptr_type)
2096 : {
2097 453 : gcc_assert (POINTER_TYPE_P (ptr_type));
2098 :
2099 : /* Some targets have address spaces in which it's valid
2100 : to dereference zero. */
2101 453 : addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (ptr_type));
2102 453 : if (!ADDR_SPACE_GENERIC_P (as)
2103 453 : && targetm.addr_space.zero_address_valid (as))
2104 : return true;
2105 :
2106 : /* Invalid. */
2107 : return false;
2108 : }
2109 :
2110 : /* Implementation of state_machine::on_stmt vfunc for malloc_state_machine. */
2111 :
2112 : bool
2113 264045 : malloc_state_machine::on_stmt (sm_context &sm_ctxt,
2114 : const gimple *stmt) const
2115 : {
2116 264045 : if (const gcall *call_stmt = dyn_cast <const gcall *> (stmt))
2117 49660 : if (tree callee_fndecl = sm_ctxt.get_fndecl_for_call (*call_stmt))
2118 : {
2119 45860 : const gcall &call = *call_stmt;
2120 :
2121 45860 : if (known_allocator_p (callee_fndecl, call))
2122 : {
2123 6444 : on_allocator_call (sm_ctxt, call, &m_free);
2124 6444 : return true;
2125 : }
2126 :
2127 39416 : if (!is_placement_new_p (call))
2128 : {
2129 78808 : bool returns_nonnull = !TREE_NOTHROW (callee_fndecl)
2130 39404 : && flag_exceptions;
2131 39404 : if (is_named_call_p (callee_fndecl, "operator new"))
2132 108 : on_allocator_call (sm_ctxt, call,
2133 : &m_scalar_delete, returns_nonnull);
2134 39296 : else if (is_named_call_p (callee_fndecl, "operator new []"))
2135 42 : on_allocator_call (sm_ctxt, call,
2136 : &m_vector_delete, returns_nonnull);
2137 : }
2138 :
2139 39416 : if (is_named_call_p (callee_fndecl, "operator delete", call, 1)
2140 39416 : || is_named_call_p (callee_fndecl, "operator delete", call, 2))
2141 : {
2142 176 : on_deallocator_call (sm_ctxt, call,
2143 : &m_scalar_delete.m_deallocator, 0);
2144 176 : return true;
2145 : }
2146 39240 : else if (is_named_call_p (callee_fndecl, "operator delete []", call, 1))
2147 : {
2148 36 : on_deallocator_call (sm_ctxt, call,
2149 : &m_vector_delete.m_deallocator, 0);
2150 36 : return true;
2151 : }
2152 :
2153 39204 : if (is_named_call_p (callee_fndecl, "alloca", call, 1)
2154 39204 : || is_named_call_p (callee_fndecl, "__builtin_alloca", call, 1))
2155 : {
2156 157 : tree lhs = gimple_call_lhs (&call);
2157 157 : if (lhs)
2158 157 : sm_ctxt.on_transition (lhs, m_start, m_non_heap);
2159 157 : return true;
2160 : }
2161 :
2162 39047 : if (is_named_call_p (callee_fndecl, "free", call, 1)
2163 36585 : || is_std_named_call_p (callee_fndecl, "free", call, 1)
2164 75626 : || is_named_call_p (callee_fndecl, "__builtin_free", call, 1))
2165 : {
2166 10970 : on_deallocator_call (sm_ctxt, call,
2167 : &m_free.m_deallocator, 0);
2168 10970 : return true;
2169 : }
2170 :
2171 28077 : if (is_named_call_p (callee_fndecl, "realloc", call, 2)
2172 27754 : || is_std_named_call_p (callee_fndecl, "realloc", call, 2)
2173 55831 : || is_named_call_p (callee_fndecl, "__builtin_realloc", call, 2))
2174 : {
2175 334 : on_realloc_call (sm_ctxt, call);
2176 334 : return true;
2177 : }
2178 :
2179 27743 : if (unaffected_by_call_p (callee_fndecl))
2180 : return true;
2181 :
2182 : /* Cast away const-ness for cache-like operations. */
2183 27735 : malloc_state_machine *mutable_this
2184 : = const_cast <malloc_state_machine *> (this);
2185 :
2186 : /* Handle interesting attributes of the callee_fndecl,
2187 : or prioritize those of the builtin that callee_fndecl is expected
2188 : to be.
2189 : Might want this to be controlled by a flag. */
2190 27735 : {
2191 27735 : tree fndecl = callee_fndecl;
2192 : /* If call is recognized as a builtin known_function, use that
2193 : builtin's function_decl. */
2194 27735 : if (const region_model *old_model = sm_ctxt.get_old_region_model ())
2195 55470 : if (const builtin_known_function *builtin_kf
2196 27735 : = old_model->get_builtin_kf (call))
2197 2367 : fndecl = builtin_kf->builtin_decl ();
2198 :
2199 : /* Handle "__attribute__((malloc(FOO)))". */
2200 55470 : if (const deallocator_set *deallocators
2201 : = mutable_this->get_or_create_custom_deallocator_set
2202 27735 : (fndecl))
2203 : {
2204 228 : tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (fndecl));
2205 228 : bool returns_nonnull
2206 228 : = lookup_attribute ("returns_nonnull", attrs);
2207 228 : on_allocator_call (sm_ctxt, call, deallocators, returns_nonnull);
2208 : }
2209 :
2210 : /* Check for this after nonnull, so that if we have both
2211 : then we transition to "freed", rather than "checked". */
2212 27735 : unsigned dealloc_argno = fndecl_dealloc_argno (fndecl);
2213 27735 : if (dealloc_argno != UINT_MAX)
2214 : {
2215 191 : const deallocator *d
2216 191 : = mutable_this->get_or_create_deallocator (fndecl);
2217 191 : on_deallocator_call (sm_ctxt, call, d, dealloc_argno);
2218 : }
2219 : }
2220 : }
2221 :
2222 : /* Look for pointers explicitly being compared against zero
2223 : that are in state assumed_non_null i.e. we already defererenced
2224 : them.
2225 : We have to do this check here, rather than in on_condition
2226 : because we add a constraint that the pointer is non-null when
2227 : dereferencing it, and this makes the apply_constraints_for_gcond
2228 : find known-true and known-false conditions; on_condition is only
2229 : called when adding new constraints. */
2230 245920 : if (const gcond *cond_stmt = dyn_cast <const gcond *> (stmt))
2231 : {
2232 38677 : enum tree_code op = gimple_cond_code (cond_stmt);
2233 38677 : if (op == EQ_EXPR || op == NE_EXPR)
2234 : {
2235 27886 : tree lhs = gimple_cond_lhs (cond_stmt);
2236 27886 : tree rhs = gimple_cond_rhs (cond_stmt);
2237 27886 : if (any_pointer_p (lhs)
2238 9748 : && any_pointer_p (rhs)
2239 37634 : && zerop (rhs))
2240 : {
2241 9058 : state_t state = sm_ctxt.get_state (lhs);
2242 9058 : if (assumed_non_null_p (state))
2243 206 : maybe_complain_about_deref_before_check
2244 206 : (sm_ctxt,
2245 : (const assumed_non_null_state *)state,
2246 : lhs);
2247 : }
2248 : }
2249 : }
2250 :
2251 245920 : if (tree lhs = sm_ctxt.is_zero_assignment (stmt))
2252 15838 : if (any_pointer_p (lhs))
2253 3007 : on_zero_assignment (sm_ctxt, lhs);
2254 :
2255 : /* Handle dereferences. */
2256 2000645 : for (unsigned i = 0; i < gimple_num_ops (stmt); i++)
2257 : {
2258 1754725 : tree op = gimple_op (stmt, i);
2259 1754725 : if (!op)
2260 131868 : continue;
2261 1622857 : if (TREE_CODE (op) == COMPONENT_REF)
2262 16159 : op = TREE_OPERAND (op, 0);
2263 :
2264 1622857 : if (TREE_CODE (op) == MEM_REF)
2265 : {
2266 33318 : tree arg = TREE_OPERAND (op, 0);
2267 :
2268 33318 : state_t state = sm_ctxt.get_state (arg);
2269 33318 : if (state == m_start)
2270 22385 : maybe_assume_non_null (sm_ctxt, arg);
2271 10933 : else if (unchecked_p (state))
2272 : {
2273 259 : if (!zero_address_valid_p (TREE_TYPE (arg)))
2274 : {
2275 259 : tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
2276 259 : sm_ctxt.warn (arg,
2277 259 : std::make_unique<possible_null_deref> (*this,
2278 : diag_arg));
2279 259 : const allocation_state *astate = as_a_allocation_state (state);
2280 259 : sm_ctxt.set_next_state (arg, astate->get_nonnull ());
2281 : }
2282 : }
2283 10674 : else if (state == m_null)
2284 : {
2285 194 : if (!zero_address_valid_p (TREE_TYPE (arg)))
2286 : {
2287 192 : tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
2288 192 : sm_ctxt.warn (arg,
2289 192 : std::make_unique<null_deref> (*this, diag_arg));
2290 192 : sm_ctxt.set_next_state (arg, m_stop);
2291 : }
2292 : }
2293 1754809 : else if (freed_p (state))
2294 : {
2295 84 : tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
2296 84 : const allocation_state *astate = as_a_allocation_state (state);
2297 84 : sm_ctxt.warn (arg,
2298 : std::make_unique<use_after_free>
2299 84 : (*this, diag_arg, astate->m_deallocator));
2300 84 : sm_ctxt.set_next_state (arg, m_stop);
2301 : }
2302 : }
2303 : }
2304 : return false;
2305 : }
2306 :
2307 : /* Given a check against null of PTR in assumed-non-null state STATE,
2308 : potentially add a deref_before_check warning to SM_CTXT. */
2309 :
2310 : void
2311 206 : malloc_state_machine::
2312 : maybe_complain_about_deref_before_check (sm_context &sm_ctxt,
2313 : const assumed_non_null_state *state,
2314 : tree ptr) const
2315 : {
2316 206 : const region_model *model = sm_ctxt.get_old_region_model ();
2317 206 : if (!model)
2318 67 : return;
2319 :
2320 : /* Don't complain if the current frame (where the check is occurring) is
2321 : deeper than the frame in which the "not null" assumption was made.
2322 : This suppress false positives for cases like:
2323 :
2324 : void foo (struct s *p)
2325 : {
2326 : int val = s->some_field; // deref here
2327 : shared_helper (p);
2328 : }
2329 :
2330 : where "shared_helper" has:
2331 :
2332 : void shared_helper (struct s *p)
2333 : {
2334 : if (!p) // check here
2335 : return;
2336 : // etc
2337 : }
2338 :
2339 : since the check in "shared_helper" is OK. */
2340 206 : const frame_region *checked_in_frame = model->get_current_frame ();
2341 206 : const frame_region *assumed_nonnull_in_frame = state->m_frame;
2342 206 : if (checked_in_frame->get_index () > assumed_nonnull_in_frame->get_index ())
2343 : return;
2344 :
2345 : /* Don't complain if code was inlined from another function, to avoid
2346 : similar false positives involving shared helper functions. */
2347 147 : if (location_t loc = sm_ctxt.get_emission_location ())
2348 : {
2349 147 : inlining_info info (loc);
2350 147 : if (info.get_extra_frames () > 0)
2351 8 : return;
2352 : }
2353 :
2354 139 : tree diag_ptr = sm_ctxt.get_diagnostic_tree (ptr);
2355 139 : if (diag_ptr)
2356 139 : sm_ctxt.warn
2357 139 : (ptr,
2358 139 : std::make_unique<deref_before_check> (*this, diag_ptr));
2359 139 : sm_ctxt.set_next_state (ptr, m_stop);
2360 : }
2361 :
2362 : /* Handle a call to an allocator.
2363 : RETURNS_NONNULL is true if CALL is to a fndecl known to have
2364 : __attribute__((returns_nonnull)). */
2365 :
2366 : void
2367 6822 : malloc_state_machine::on_allocator_call (sm_context &sm_ctxt,
2368 : const gcall &call,
2369 : const deallocator_set *deallocators,
2370 : bool returns_nonnull) const
2371 : {
2372 6822 : tree lhs = gimple_call_lhs (&call);
2373 6822 : if (lhs)
2374 : {
2375 6822 : if (sm_ctxt.get_state (lhs) == m_start)
2376 6798 : sm_ctxt.set_next_state (lhs,
2377 : (returns_nonnull
2378 : ? deallocators->m_nonnull
2379 : : deallocators->m_unchecked));
2380 : }
2381 : else
2382 : {
2383 : /* TODO: report leak. */
2384 : }
2385 6822 : }
2386 :
2387 : /* Handle deallocations of non-heap pointers.
2388 : non-heap -> stop, with warning. */
2389 :
2390 : void
2391 61 : malloc_state_machine::handle_free_of_non_heap (sm_context &sm_ctxt,
2392 : const gcall &,
2393 : tree arg,
2394 : const deallocator *d) const
2395 : {
2396 61 : tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
2397 61 : const region *freed_reg = nullptr;
2398 61 : if (const program_state *old_state = sm_ctxt.get_old_program_state ())
2399 : {
2400 61 : const region_model *old_model = old_state->m_region_model;
2401 61 : const svalue *ptr_sval = old_model->get_rvalue (arg, nullptr);
2402 61 : freed_reg = old_model->deref_rvalue (ptr_sval, arg, nullptr);
2403 : }
2404 61 : sm_ctxt.warn (arg,
2405 : std::make_unique<free_of_non_heap>
2406 61 : (*this, diag_arg, freed_reg, d->m_name));
2407 61 : sm_ctxt.set_next_state (arg, m_stop);
2408 61 : }
2409 :
2410 : void
2411 11373 : malloc_state_machine::on_deallocator_call (sm_context &sm_ctxt,
2412 : const gcall &call,
2413 : const deallocator *d,
2414 : unsigned argno) const
2415 : {
2416 11373 : if (argno >= gimple_call_num_args (&call))
2417 : return;
2418 11373 : tree arg = gimple_call_arg (&call, argno);
2419 :
2420 11373 : state_t state = sm_ctxt.get_state (arg);
2421 :
2422 : /* start/assumed_non_null/unchecked/nonnull -> freed. */
2423 21703 : if (state == m_start || assumed_non_null_p (state))
2424 1071 : sm_ctxt.set_next_state (arg, d->m_freed);
2425 10302 : else if (unchecked_p (state) || nonnull_p (state))
2426 : {
2427 5480 : const allocation_state *astate = as_a_allocation_state (state);
2428 5480 : gcc_assert (astate->m_deallocators);
2429 5480 : if (!astate->m_deallocators->contains_p (d))
2430 : {
2431 : /* Wrong allocator. */
2432 72 : tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
2433 72 : sm_ctxt.warn (arg,
2434 : std::make_unique<mismatching_deallocation>
2435 72 : (*this, diag_arg,
2436 72 : astate->m_deallocators,
2437 : d));
2438 : }
2439 5480 : sm_ctxt.set_next_state (arg, d->m_freed);
2440 : }
2441 :
2442 : /* Keep state "null" as-is, rather than transitioning to "freed";
2443 : we don't want to complain about double-free of NULL. */
2444 4822 : else if (state == d->m_freed)
2445 : {
2446 : /* freed -> stop, with warning. */
2447 4428 : tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
2448 4428 : sm_ctxt.warn (arg,
2449 4428 : std::make_unique<double_free> (*this, diag_arg, d->m_name));
2450 4428 : sm_ctxt.set_next_state (arg, m_stop);
2451 : }
2452 394 : else if (state == m_non_heap)
2453 : {
2454 : /* non-heap -> stop, with warning. */
2455 57 : handle_free_of_non_heap (sm_ctxt, call, arg, d);
2456 : }
2457 : }
2458 :
2459 : /* Handle a call to "realloc".
2460 : Check for free of non-heap or mismatching allocators,
2461 : transitioning to the "stop" state for such cases.
2462 :
2463 : Otherwise, kf_realloc::impl_call_post will later
2464 : get called (which will handle other sm-state transitions
2465 : when the state is bifurcated). */
2466 :
2467 : void
2468 334 : malloc_state_machine::on_realloc_call (sm_context &sm_ctxt,
2469 : const gcall &call) const
2470 : {
2471 334 : const unsigned argno = 0;
2472 334 : const deallocator *d = &m_realloc;
2473 :
2474 334 : tree arg = gimple_call_arg (&call, argno);
2475 :
2476 334 : state_t state = sm_ctxt.get_state (arg);
2477 :
2478 334 : if (unchecked_p (state) || nonnull_p (state))
2479 : {
2480 138 : const allocation_state *astate = as_a_allocation_state (state);
2481 138 : gcc_assert (astate->m_deallocators);
2482 138 : if (!astate->m_deallocators->contains_p (&m_free.m_deallocator))
2483 : {
2484 : /* Wrong allocator. */
2485 16 : tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
2486 16 : sm_ctxt.warn (arg,
2487 : std::make_unique<mismatching_deallocation>
2488 16 : (*this, diag_arg,
2489 16 : astate->m_deallocators, d));
2490 16 : sm_ctxt.set_next_state (arg, m_stop);
2491 16 : if (path_context *path_ctxt = sm_ctxt.get_path_context ())
2492 16 : path_ctxt->terminate_path ();
2493 : }
2494 : }
2495 196 : else if (state == m_free.m_deallocator.m_freed)
2496 : {
2497 : /* freed -> stop, with warning. */
2498 4 : tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
2499 4 : sm_ctxt.warn (arg,
2500 4 : std::make_unique<double_free> (*this, diag_arg, "free"));
2501 4 : sm_ctxt.set_next_state (arg, m_stop);
2502 4 : if (path_context *path_ctxt = sm_ctxt.get_path_context ())
2503 4 : path_ctxt->terminate_path ();
2504 : }
2505 192 : else if (state == m_non_heap)
2506 : {
2507 : /* non-heap -> stop, with warning. */
2508 4 : handle_free_of_non_heap (sm_ctxt, call, arg, d);
2509 4 : if (path_context *path_ctxt = sm_ctxt.get_path_context ())
2510 4 : path_ctxt->terminate_path ();
2511 : }
2512 334 : }
2513 :
2514 : /* Implementation of state_machine::on_phi vfunc for malloc_state_machine. */
2515 :
2516 : void
2517 0 : malloc_state_machine::on_phi (sm_context &sm_ctxt,
2518 : const gphi *phi,
2519 : tree rhs) const
2520 : {
2521 0 : if (zerop (rhs))
2522 : {
2523 0 : tree lhs = gimple_phi_result (phi);
2524 0 : on_zero_assignment (sm_ctxt, lhs);
2525 : }
2526 0 : }
2527 :
2528 : void
2529 57208 : malloc_state_machine::check_call_preconditions (sm_context &sm_ctxt,
2530 : const call_details &cd) const
2531 : {
2532 57208 : tree fndecl = cd.get_fndecl_for_call ();
2533 57208 : if (!fndecl)
2534 : return;
2535 :
2536 53408 : const tree fntype = TREE_TYPE (fndecl);
2537 53408 : const unsigned num_args = cd.num_args ();
2538 :
2539 : /* Handle "__attribute__((nonnull))". */
2540 53408 : if (bitmap nonnull_args = get_nonnull_args (fntype))
2541 : {
2542 16772 : for (unsigned i = 0; i < num_args; i++)
2543 : {
2544 11412 : tree arg = cd.get_arg_tree (i);
2545 11412 : if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
2546 3348 : continue;
2547 : /* If we have a nonnull-args, and either all pointers, or
2548 : just the specified pointers. */
2549 8064 : if (bitmap_empty_p (nonnull_args)
2550 8064 : || bitmap_bit_p (nonnull_args, i))
2551 6442 : handle_nonnull (sm_ctxt, fndecl, arg, i);
2552 : }
2553 5360 : BITMAP_FREE (nonnull_args);
2554 : }
2555 :
2556 : /* Handle __attribute__((nonnull_if_nonzero (x, y))). */
2557 53408 : if (fntype)
2558 53408 : for (tree attrs = TYPE_ATTRIBUTES (fntype);
2559 55707 : (attrs = lookup_attribute ("nonnull_if_nonzero", attrs));
2560 2299 : attrs = TREE_CHAIN (attrs))
2561 : {
2562 2299 : tree args = TREE_VALUE (attrs);
2563 2299 : unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1;
2564 2299 : unsigned int idx2
2565 2299 : = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1;
2566 2299 : unsigned int idx3 = idx2;
2567 2299 : if (tree chain2 = TREE_CHAIN (TREE_CHAIN (args)))
2568 808 : idx3 = TREE_INT_CST_LOW (TREE_VALUE (chain2)) - 1;
2569 2299 : if (idx < num_args
2570 : && idx2 < num_args
2571 2299 : && idx3 < num_args)
2572 : {
2573 2299 : tree arg = cd.get_arg_tree (idx);
2574 2299 : tree arg2 = cd.get_arg_tree (idx2);
2575 2299 : tree arg3 = cd.get_arg_tree (idx3);
2576 2299 : if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE
2577 2299 : || !INTEGRAL_TYPE_P (TREE_TYPE (arg2))
2578 2299 : || !INTEGRAL_TYPE_P (TREE_TYPE (arg3))
2579 2299 : || integer_zerop (arg2)
2580 4594 : || integer_zerop (arg3))
2581 4 : continue;
2582 2295 : if (integer_nonzerop (arg2) && integer_nonzerop (arg3))
2583 : ;
2584 : else
2585 : /* FIXME: Use ranger here to query arg2 and arg3
2586 : ranges? */
2587 1303 : continue;
2588 992 : handle_nonnull (sm_ctxt, fndecl, arg, idx);
2589 : }
2590 : }
2591 : }
2592 :
2593 : /* Implementation of state_machine::on_condition vfunc for malloc_state_machine.
2594 : Potentially transition state 'unchecked' to 'nonnull' or to 'null'. */
2595 :
2596 : void
2597 34599 : malloc_state_machine::on_condition (sm_context &sm_ctxt,
2598 : const svalue *lhs,
2599 : enum tree_code op,
2600 : const svalue *rhs) const
2601 : {
2602 34599 : if (!rhs->all_zeroes_p ())
2603 : return;
2604 :
2605 21184 : if (!any_pointer_p (lhs))
2606 : return;
2607 8190 : if (!any_pointer_p (rhs))
2608 : return;
2609 :
2610 8190 : if (op == NE_EXPR)
2611 : {
2612 4461 : log ("got 'ARG != 0' match");
2613 4461 : state_t s = sm_ctxt.get_state (lhs);
2614 4461 : if (unchecked_p (s))
2615 : {
2616 1211 : const allocation_state *astate = as_a_allocation_state (s);
2617 1211 : sm_ctxt.set_next_state (lhs, astate->get_nonnull ());
2618 : }
2619 : }
2620 3729 : else if (op == EQ_EXPR)
2621 : {
2622 3729 : log ("got 'ARG == 0' match");
2623 3729 : state_t s = sm_ctxt.get_state (lhs);
2624 3729 : if (unchecked_p (s))
2625 1194 : sm_ctxt.set_next_state (lhs, m_null);
2626 : }
2627 : }
2628 :
2629 : /* Implementation of state_machine::on_pop_frame vfunc for malloc_state_machine.
2630 : Clear any "assumed-non-null" state where the assumption happened in
2631 : FRAME_REG. */
2632 :
2633 : void
2634 23470 : malloc_state_machine::on_pop_frame (sm_state_map *smap,
2635 : const frame_region *frame_reg) const
2636 : {
2637 23470 : hash_set<const svalue *> svals_to_clear;
2638 34327 : for (auto kv : *smap)
2639 : {
2640 10857 : const svalue *sval = kv.first;
2641 10857 : state_t state = kv.second.m_state;
2642 15814 : if (assumed_non_null_p (state))
2643 : {
2644 4957 : const assumed_non_null_state *assumed_state
2645 : = (const assumed_non_null_state *)state;
2646 4957 : if (frame_reg == assumed_state->m_frame)
2647 2910 : svals_to_clear.add (sval);
2648 : }
2649 : }
2650 49850 : for (auto sval : svals_to_clear)
2651 2910 : smap->clear_any_state (sval);
2652 23470 : }
2653 :
2654 : /* Implementation of state_machine::can_purge_p vfunc for malloc_state_machine.
2655 : Don't allow purging of pointers in state 'unchecked' or 'nonnull'
2656 : (to avoid false leak reports). */
2657 :
2658 : bool
2659 1463922 : malloc_state_machine::can_purge_p (state_t s) const
2660 : {
2661 1463922 : enum resource_state rs = get_rs (s);
2662 1463922 : return rs != RS_UNCHECKED && rs != RS_NONNULL;
2663 : }
2664 :
2665 : /* Implementation of state_machine::on_leak vfunc for malloc_state_machine
2666 : (for complaining about leaks of pointers in state 'unchecked' and
2667 : 'nonnull'). */
2668 :
2669 : std::unique_ptr<pending_diagnostic>
2670 883 : malloc_state_machine::on_leak (tree var,
2671 : const program_state *,
2672 : const program_state *new_state) const
2673 : {
2674 883 : return std::make_unique<malloc_leak> (*this, var, new_state);
2675 : }
2676 :
2677 : /* Implementation of state_machine::reset_when_passed_to_unknown_fn_p vfunc
2678 : for malloc_state_machine. */
2679 :
2680 : bool
2681 20362 : malloc_state_machine::reset_when_passed_to_unknown_fn_p (state_t s,
2682 : bool is_mutable) const
2683 : {
2684 : /* An on-stack ptr doesn't stop being stack-allocated when passed to an
2685 : unknown fn. */
2686 20362 : if (s == m_non_heap)
2687 0 : return false;
2688 :
2689 : /* Otherwise, pointers passed as non-const can be freed. */
2690 : return is_mutable;
2691 : }
2692 :
2693 : /* Implementation of state_machine::maybe_get_merged_states_nonequal vfunc
2694 : for malloc_state_machine.
2695 :
2696 : Support discarding "assumed-non-null" states when merging with
2697 : start state. */
2698 :
2699 : state_machine::state_t
2700 132235 : malloc_state_machine::maybe_get_merged_states_nonequal (state_t state_a,
2701 : state_t state_b) const
2702 : {
2703 132235 : if (assumed_non_null_p (state_a) && state_b == m_start)
2704 : return m_start;
2705 137660 : if (state_a == m_start && assumed_non_null_p (state_b))
2706 24680 : return m_start;
2707 : return nullptr;
2708 : }
2709 :
2710 : /* Return true if calls to FNDECL are known to not affect this sm-state. */
2711 :
2712 : bool
2713 27743 : malloc_state_machine::unaffected_by_call_p (tree fndecl)
2714 : {
2715 : /* A set of functions that are known to not affect allocation
2716 : status, even if we haven't fully modelled the rest of their
2717 : behavior yet. */
2718 27743 : static const char * const funcnames[] = {
2719 : /* This array must be kept sorted. */
2720 : "strsep",
2721 : };
2722 27743 : const size_t count = ARRAY_SIZE (funcnames);
2723 27743 : function_set fs (funcnames, count);
2724 :
2725 27743 : if (fs.contains_decl_p (fndecl))
2726 : return true;
2727 :
2728 : return false;
2729 : }
2730 :
2731 : /* Shared logic for handling GIMPLE_ASSIGNs and GIMPLE_PHIs that
2732 : assign zero to LHS. */
2733 :
2734 : void
2735 3007 : malloc_state_machine::on_zero_assignment (sm_context &sm_ctxt,
2736 : tree lhs) const
2737 : {
2738 3007 : state_t s = sm_ctxt.get_state (lhs);
2739 3007 : enum resource_state rs = get_rs (s);
2740 3007 : if (rs == RS_START
2741 3007 : || rs == RS_UNCHECKED
2742 : || rs == RS_NONNULL
2743 134 : || rs == RS_FREED)
2744 3001 : sm_ctxt.set_next_state (lhs, m_null);
2745 3007 : }
2746 :
2747 : /* Special-case hook for handling realloc, for the "success with move to
2748 : a new buffer" case, marking OLD_PTR_SVAL as freed and NEW_PTR_SVAL as
2749 : non-null.
2750 :
2751 : This is similar to on_deallocator_call and on_allocator_call,
2752 : but the checks happen in on_realloc_call, and by splitting the states. */
2753 :
2754 : void
2755 310 : malloc_state_machine::
2756 : on_realloc_with_move (region_model *model,
2757 : sm_state_map *smap,
2758 : const svalue *old_ptr_sval,
2759 : const svalue *new_ptr_sval,
2760 : const extrinsic_state &ext_state) const
2761 : {
2762 310 : smap->set_state (model, old_ptr_sval,
2763 310 : m_free.m_deallocator.m_freed,
2764 : nullptr, ext_state);
2765 :
2766 310 : smap->set_state (model, new_ptr_sval,
2767 310 : m_free.m_nonnull,
2768 : nullptr, ext_state);
2769 310 : }
2770 :
2771 : /* Hook for get_or_create_region_for_heap_alloc for the case when we want
2772 : ptr_sval to mark a newly created region as assumed non null on malloc SM. */
2773 : void
2774 0 : malloc_state_machine::transition_ptr_sval_non_null (region_model *model,
2775 : sm_state_map *smap,
2776 : const svalue *new_ptr_sval,
2777 : const extrinsic_state &ext_state) const
2778 : {
2779 0 : smap->set_state (model, new_ptr_sval, m_free.m_nonnull, nullptr, ext_state);
2780 0 : }
2781 :
2782 : static enum custom_sarif_properties::state_graphs::node::dynalloc_state_t
2783 44 : get_dynalloc_state_for_state (enum resource_state rs)
2784 : {
2785 44 : switch (rs)
2786 : {
2787 0 : default:
2788 0 : gcc_unreachable ();
2789 : case RS_START:
2790 : case RS_NULL:
2791 : case RS_NON_HEAP:
2792 : case RS_STOP:
2793 : return state_node_properties::dynalloc_state_t::unknown;
2794 :
2795 : case RS_ASSUMED_NON_NULL:
2796 : return state_node_properties::dynalloc_state_t::nonnull;
2797 :
2798 : case RS_UNCHECKED:
2799 : return state_node_properties::dynalloc_state_t::unchecked;
2800 : case RS_NONNULL:
2801 : return state_node_properties::dynalloc_state_t::nonnull;
2802 : case RS_FREED:
2803 : return state_node_properties::dynalloc_state_t::freed;
2804 : }
2805 : }
2806 :
2807 : void
2808 44 : malloc_state_machine::
2809 : add_state_to_state_graph (analyzer_state_graph &out_state_graph,
2810 : const svalue &sval,
2811 : state_machine::state_t state) const
2812 : {
2813 44 : if (const region *reg = sval.maybe_get_region ())
2814 : {
2815 44 : auto ®_node = out_state_graph.get_or_create_state_node (*reg);
2816 44 : auto alloc_state = as_a_allocation_state (state);
2817 44 : gcc_assert (alloc_state);
2818 :
2819 44 : reg_node.set_property (state_node_properties::dynalloc_state_prop,
2820 44 : get_dynalloc_state_for_state (alloc_state->m_rs));
2821 :
2822 44 : if (alloc_state->m_deallocators)
2823 : {
2824 42 : pretty_printer pp;
2825 42 : alloc_state->m_deallocators->dump_to_pp (&pp);
2826 42 : reg_node.set_property (state_node_properties::expected_deallocators,
2827 : pp_formatted_text (&pp));
2828 42 : }
2829 44 : if (alloc_state->m_deallocator)
2830 2 : reg_node.set_property (state_node_properties::deallocator,
2831 2 : alloc_state->m_deallocator->m_name);
2832 : }
2833 44 : }
2834 :
2835 : } // anonymous namespace
2836 :
2837 : /* Internal interface to this file. */
2838 :
2839 : std::unique_ptr<state_machine>
2840 3389 : make_malloc_state_machine (logger *logger)
2841 : {
2842 3389 : return std::make_unique<malloc_state_machine> (logger);
2843 : }
2844 :
2845 : /* Specialcase hook for handling realloc, for use by
2846 : kf_realloc::impl_call_post::success_with_move::update_model. */
2847 :
2848 : void
2849 394 : region_model::on_realloc_with_move (const call_details &cd,
2850 : const svalue *old_ptr_sval,
2851 : const svalue *new_ptr_sval)
2852 : {
2853 394 : region_model_context *ctxt = cd.get_ctxt ();
2854 394 : if (!ctxt)
2855 84 : return;
2856 312 : const extrinsic_state *ext_state = ctxt->get_ext_state ();
2857 312 : if (!ext_state)
2858 : return;
2859 :
2860 310 : sm_state_map *smap;
2861 310 : const state_machine *sm;
2862 310 : unsigned sm_idx;
2863 310 : if (!ctxt->get_malloc_map (&smap, &sm, &sm_idx))
2864 : return;
2865 :
2866 310 : gcc_assert (smap);
2867 310 : gcc_assert (sm);
2868 :
2869 310 : const malloc_state_machine &malloc_sm
2870 : = (const malloc_state_machine &)*sm;
2871 :
2872 310 : malloc_sm.on_realloc_with_move (this,
2873 : smap,
2874 : old_ptr_sval,
2875 : new_ptr_sval,
2876 : *ext_state);
2877 : }
2878 :
2879 : /* Moves ptr_sval from start to assumed non-null, for use by
2880 : region_model::get_or_create_region_for_heap_alloc. */
2881 : void
2882 0 : region_model::transition_ptr_sval_non_null (region_model_context *ctxt,
2883 : const svalue *ptr_sval)
2884 : {
2885 0 : if (!ctxt)
2886 0 : return;
2887 0 : const extrinsic_state *ext_state = ctxt->get_ext_state ();
2888 0 : if (!ext_state)
2889 : return;
2890 :
2891 0 : sm_state_map *smap;
2892 0 : const state_machine *sm;
2893 0 : unsigned sm_idx;
2894 0 : if (!ctxt->get_malloc_map (&smap, &sm, &sm_idx))
2895 : return;
2896 :
2897 0 : gcc_assert (smap);
2898 0 : gcc_assert (sm);
2899 :
2900 0 : const malloc_state_machine &malloc_sm = (const malloc_state_machine &)*sm;
2901 :
2902 0 : malloc_sm.transition_ptr_sval_non_null (this, smap, ptr_sval, *ext_state);
2903 : }
2904 :
2905 : } // namespace ana
2906 :
2907 : #endif /* #if ENABLE_ANALYZER */
|