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