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