]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/analyzer/sm-malloc.cc
analyzer: add -fdump-analyzer-json
[thirdparty/gcc.git] / gcc / analyzer / sm-malloc.cc
1 /* A state machine for detecting misuses of the malloc/free API.
2 Copyright (C) 2019-2020 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 "config.h"
22 #include "system.h"
23 #include "coretypes.h"
24 #include "tree.h"
25 #include "function.h"
26 #include "basic-block.h"
27 #include "gimple.h"
28 #include "options.h"
29 #include "bitmap.h"
30 #include "diagnostic-path.h"
31 #include "diagnostic-metadata.h"
32 #include "function.h"
33 #include "json.h"
34 #include "analyzer/analyzer.h"
35 #include "diagnostic-event-id.h"
36 #include "analyzer/analyzer-logging.h"
37 #include "analyzer/sm.h"
38 #include "analyzer/pending-diagnostic.h"
39 #include "tristate.h"
40 #include "selftest.h"
41 #include "analyzer/call-string.h"
42 #include "analyzer/program-point.h"
43 #include "analyzer/store.h"
44 #include "analyzer/region-model.h"
45
46 #if ENABLE_ANALYZER
47
48 namespace ana {
49
50 namespace {
51
52 class api;
53 class malloc_state_machine;
54
55 /* An enum for discriminating between different kinds of allocation_state. */
56
57 enum resource_state
58 {
59 /* States that are independent of api. */
60
61 /* The start state. */
62 RS_START,
63
64 /* State for a pointer that's known to be NULL. */
65 RS_NULL,
66
67 /* State for a pointer that's known to not be on the heap (e.g. to a local
68 or global). */
69 RS_NON_HEAP,
70
71 /* Stop state, for pointers we don't want to track any more. */
72 RS_STOP,
73
74 /* States that relate to a specific api. */
75
76 /* State for a pointer returned from the api's allocator that hasn't
77 been checked for NULL.
78 It could be a pointer to heap-allocated memory, or could be NULL. */
79 RS_UNCHECKED,
80
81 /* State for a pointer returned from the api's allocator,
82 known to be non-NULL. */
83 RS_NONNULL,
84
85 /* State for a pointer passed to the api's deallocator. */
86 RS_FREED
87 };
88
89 /* Custom state subclass, which can optionally refer to an an api. */
90
91 struct allocation_state : public state_machine::state
92 {
93 allocation_state (const char *name, unsigned id,
94 enum resource_state rs, const api *a)
95 : state (name, id), m_rs (rs), m_api (a)
96 {}
97
98 void dump_to_pp (pretty_printer *pp) const FINAL OVERRIDE;
99
100 const allocation_state *get_nonnull () const;
101
102 enum resource_state m_rs;
103 const api *m_api;
104 };
105
106 /* An enum for choosing which wording to use in various diagnostics
107 when describing deallocations. */
108
109 enum wording
110 {
111 WORDING_FREED,
112 WORDING_DELETED
113 };
114
115 /* Represents a particular family of API calls for allocating/deallocating
116 heap memory that should be matched e.g.
117 - malloc/free
118 - scalar new/delete
119 - vector new[]/delete[]
120 etc.
121
122 We track the expected deallocation function, but not the allocation
123 function - there could be more than one allocator per deallocator. For
124 example, there could be dozens of allocators for "free" beyond just
125 malloc e.g. calloc, xstrdup, etc. We don't want to explode the number
126 of states by tracking individual allocators in the exploded graph;
127 we merely want to track "this value expects to have 'free' called on it".
128 Perhaps we can reconstruct which allocator was used later, when emitting
129 the path, if it's necessary for precision of wording of diagnostics. */
130
131 struct api
132 {
133 api (malloc_state_machine *sm,
134 const char *name,
135 const char *dealloc_funcname,
136 enum wording wording);
137
138 /* An internal name for identifying this API in dumps. */
139 const char *m_name;
140
141 /* The name of the deallocation function, for use in diagnostics. */
142 const char *m_dealloc_funcname;
143
144 /* Which wording to use in diagnostics. */
145 enum wording m_wording;
146
147 /* Pointers to api-specific states.
148 These states are owned by the state_machine base class. */
149
150 /* State for an unchecked result from this api's allocator. */
151 state_machine::state_t m_unchecked;
152
153 /* State for a known non-NULL result from this apis's allocator. */
154 state_machine::state_t m_nonnull;
155
156 /* State for a value passed to this api's deallocator. */
157 state_machine::state_t m_freed;
158 };
159
160 /* A state machine for detecting misuses of the malloc/free API.
161
162 See sm-malloc.dot for an overview (keep this in-sync with that file). */
163
164 class malloc_state_machine : public state_machine
165 {
166 public:
167 typedef allocation_state custom_data_t;
168
169 malloc_state_machine (logger *logger);
170
171 state_t
172 add_state (const char *name, enum resource_state rs, const api *a);
173
174 bool inherited_state_p () const FINAL OVERRIDE { return false; }
175
176 state_machine::state_t
177 get_default_state (const svalue *sval) const FINAL OVERRIDE
178 {
179 if (tree cst = sval->maybe_get_constant ())
180 {
181 if (zerop (cst))
182 return m_null;
183 }
184 if (const region_svalue *ptr = sval->dyn_cast_region_svalue ())
185 {
186 const region *reg = ptr->get_pointee ();
187 const region *base_reg = reg->get_base_region ();
188 if (base_reg->get_kind () == RK_DECL
189 || base_reg->get_kind () == RK_STRING)
190 return m_non_heap;
191 }
192 return m_start;
193 }
194
195 bool on_stmt (sm_context *sm_ctxt,
196 const supernode *node,
197 const gimple *stmt) const FINAL OVERRIDE;
198
199 void on_phi (sm_context *sm_ctxt,
200 const supernode *node,
201 const gphi *phi,
202 tree rhs) const FINAL OVERRIDE;
203
204 void on_condition (sm_context *sm_ctxt,
205 const supernode *node,
206 const gimple *stmt,
207 tree lhs,
208 enum tree_code op,
209 tree rhs) const FINAL OVERRIDE;
210
211 bool can_purge_p (state_t s) const FINAL OVERRIDE;
212 pending_diagnostic *on_leak (tree var) const FINAL OVERRIDE;
213
214 bool reset_when_passed_to_unknown_fn_p (state_t s,
215 bool is_mutable) const FINAL OVERRIDE;
216
217 api m_malloc;
218 api m_scalar_new;
219 api m_vector_new;
220
221 /* States that are independent of api. */
222
223 /* State for a pointer that's known to be NULL. */
224 state_t m_null;
225
226 /* State for a pointer that's known to not be on the heap (e.g. to a local
227 or global). */
228 state_t m_non_heap; // TODO: or should this be a different state machine?
229 // or do we need child values etc?
230
231 /* Stop state, for pointers we don't want to track any more. */
232 state_t m_stop;
233
234 private:
235 void on_allocator_call (sm_context *sm_ctxt,
236 const gcall *call,
237 const api &ap) const;
238 void on_deallocator_call (sm_context *sm_ctxt,
239 const supernode *node,
240 const gcall *call,
241 const api &ap) const;
242 void on_zero_assignment (sm_context *sm_ctxt,
243 const gimple *stmt,
244 tree lhs) const;
245 };
246
247 /* struct api. */
248
249 api::api (malloc_state_machine *sm,
250 const char *name,
251 const char *dealloc_funcname,
252 enum wording wording)
253 : m_name (name),
254 m_dealloc_funcname (dealloc_funcname),
255 m_wording (wording),
256 m_unchecked (sm->add_state ("unchecked", RS_UNCHECKED, this)),
257 m_nonnull (sm->add_state ("nonnull", RS_NONNULL, this)),
258 m_freed (sm->add_state ("freed", RS_FREED, this))
259 {
260 }
261
262 /* Return STATE cast to the custom state subclass, or NULL for the start state.
263 Everything should be an allocation_state apart from the start state. */
264
265 static const allocation_state *
266 dyn_cast_allocation_state (state_machine::state_t state)
267 {
268 if (state->get_id () == 0)
269 return NULL;
270 return static_cast <const allocation_state *> (state);
271 }
272
273 /* Return STATE cast to the custom state subclass, for a state that is
274 already known to not be the start state . */
275
276 static const allocation_state *
277 as_a_allocation_state (state_machine::state_t state)
278 {
279 gcc_assert (state->get_id () != 0);
280 return static_cast <const allocation_state *> (state);
281 }
282
283 /* Get the resource_state for STATE. */
284
285 static enum resource_state
286 get_rs (state_machine::state_t state)
287 {
288 if (const allocation_state *astate = dyn_cast_allocation_state (state))
289 return astate->m_rs;
290 else
291 return RS_START;
292 }
293
294 /* Return true if STATE is an unchecked result from an allocator. */
295
296 static bool
297 unchecked_p (state_machine::state_t state)
298 {
299 return get_rs (state) == RS_UNCHECKED;
300 }
301
302 /* Return true if STATE is a non-null result from an allocator. */
303
304 static bool
305 nonnull_p (state_machine::state_t state)
306 {
307 return get_rs (state) == RS_NONNULL;
308 }
309
310 /* Return true if STATE is a value that has been passed to a deallocator. */
311
312 static bool
313 freed_p (state_machine::state_t state)
314 {
315 return get_rs (state) == RS_FREED;
316 }
317
318 /* Class for diagnostics relating to malloc_state_machine. */
319
320 class malloc_diagnostic : public pending_diagnostic
321 {
322 public:
323 malloc_diagnostic (const malloc_state_machine &sm, tree arg)
324 : m_sm (sm), m_arg (arg)
325 {}
326
327 bool subclass_equal_p (const pending_diagnostic &base_other) const OVERRIDE
328 {
329 return same_tree_p (m_arg, ((const malloc_diagnostic &)base_other).m_arg);
330 }
331
332 label_text describe_state_change (const evdesc::state_change &change)
333 OVERRIDE
334 {
335 if (change.m_old_state == m_sm.get_start_state ()
336 && unchecked_p (change.m_new_state))
337 // TODO: verify that it's the allocation stmt, not a copy
338 return label_text::borrow ("allocated here");
339 if (unchecked_p (change.m_old_state)
340 && nonnull_p (change.m_new_state))
341 {
342 if (change.m_expr)
343 return change.formatted_print ("assuming %qE is non-NULL",
344 change.m_expr);
345 else
346 return change.formatted_print ("assuming %qs is non-NULL",
347 "<unknown>");
348 }
349 if (change.m_new_state == m_sm.m_null)
350 {
351 if (unchecked_p (change.m_old_state))
352 {
353 if (change.m_expr)
354 return change.formatted_print ("assuming %qE is NULL",
355 change.m_expr);
356 else
357 return change.formatted_print ("assuming %qs is NULL",
358 "<unknown>");
359 }
360 else
361 {
362 if (change.m_expr)
363 return change.formatted_print ("%qE is NULL",
364 change.m_expr);
365 else
366 return change.formatted_print ("%qs is NULL",
367 "<unknown>");
368 }
369 }
370
371 return label_text ();
372 }
373
374 protected:
375 const malloc_state_machine &m_sm;
376 tree m_arg;
377 };
378
379 /* Concrete subclass for reporting mismatching allocator/deallocator
380 diagnostics. */
381
382 class mismatching_deallocation : public malloc_diagnostic
383 {
384 public:
385 mismatching_deallocation (const malloc_state_machine &sm, tree arg,
386 const api *expected_dealloc,
387 const api *actual_dealloc)
388 : malloc_diagnostic (sm, arg),
389 m_expected_dealloc (expected_dealloc),
390 m_actual_dealloc (actual_dealloc)
391 {}
392
393 const char *get_kind () const FINAL OVERRIDE
394 {
395 return "mismatching_deallocation";
396 }
397
398 bool emit (rich_location *rich_loc) FINAL OVERRIDE
399 {
400 auto_diagnostic_group d;
401 diagnostic_metadata m;
402 m.add_cwe (762); /* CWE-762: Mismatched Memory Management Routines. */
403 return warning_meta (rich_loc, m, OPT_Wanalyzer_mismatching_deallocation,
404 "%qE should have been deallocated with %qs"
405 " but was deallocated with %qs",
406 m_arg, m_expected_dealloc->m_dealloc_funcname,
407 m_actual_dealloc->m_dealloc_funcname);
408 }
409
410 label_text describe_state_change (const evdesc::state_change &change)
411 FINAL OVERRIDE
412 {
413 if (unchecked_p (change.m_new_state))
414 {
415 m_alloc_event = change.m_event_id;
416 return change.formatted_print ("allocated here"
417 " (expects deallocation with %qs)",
418 m_expected_dealloc->m_dealloc_funcname);
419 }
420 return malloc_diagnostic::describe_state_change (change);
421 }
422
423 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
424 {
425 if (m_alloc_event.known_p ())
426 return ev.formatted_print
427 ("deallocated with %qs here;"
428 " allocation at %@ expects deallocation with %qs",
429 m_actual_dealloc->m_dealloc_funcname, &m_alloc_event,
430 m_expected_dealloc->m_dealloc_funcname);
431 return ev.formatted_print ("deallocated with %qs here",
432 m_actual_dealloc->m_dealloc_funcname);
433 }
434
435 private:
436 diagnostic_event_id_t m_alloc_event;
437 const api *m_expected_dealloc;
438 const api *m_actual_dealloc;
439 };
440
441 /* Concrete subclass for reporting double-free diagnostics. */
442
443 class double_free : public malloc_diagnostic
444 {
445 public:
446 double_free (const malloc_state_machine &sm, tree arg, const char *funcname)
447 : malloc_diagnostic (sm, arg), m_funcname (funcname)
448 {}
449
450 const char *get_kind () const FINAL OVERRIDE { return "double_free"; }
451
452 bool emit (rich_location *rich_loc) FINAL OVERRIDE
453 {
454 auto_diagnostic_group d;
455 diagnostic_metadata m;
456 m.add_cwe (415); /* CWE-415: Double Free. */
457 return warning_meta (rich_loc, m, OPT_Wanalyzer_double_free,
458 "double-%<%s%> of %qE", m_funcname, m_arg);
459 }
460
461 label_text describe_state_change (const evdesc::state_change &change)
462 FINAL OVERRIDE
463 {
464 if (freed_p (change.m_new_state))
465 {
466 m_first_free_event = change.m_event_id;
467 return change.formatted_print ("first %qs here", m_funcname);
468 }
469 return malloc_diagnostic::describe_state_change (change);
470 }
471
472 label_text describe_call_with_state (const evdesc::call_with_state &info)
473 FINAL OVERRIDE
474 {
475 if (freed_p (info.m_state))
476 return info.formatted_print
477 ("passing freed pointer %qE in call to %qE from %qE",
478 info.m_expr, info.m_callee_fndecl, info.m_caller_fndecl);
479 return label_text ();
480 }
481
482 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
483 {
484 if (m_first_free_event.known_p ())
485 return ev.formatted_print ("second %qs here; first %qs was at %@",
486 m_funcname, m_funcname,
487 &m_first_free_event);
488 return ev.formatted_print ("second %qs here", m_funcname);
489 }
490
491 private:
492 diagnostic_event_id_t m_first_free_event;
493 const char *m_funcname;
494 };
495
496 /* Abstract subclass for describing possible bad uses of NULL.
497 Responsible for describing the call that could return NULL. */
498
499 class possible_null : public malloc_diagnostic
500 {
501 public:
502 possible_null (const malloc_state_machine &sm, tree arg)
503 : malloc_diagnostic (sm, arg)
504 {}
505
506 label_text describe_state_change (const evdesc::state_change &change)
507 FINAL OVERRIDE
508 {
509 if (change.m_old_state == m_sm.get_start_state ()
510 && unchecked_p (change.m_new_state))
511 {
512 m_origin_of_unchecked_event = change.m_event_id;
513 return label_text::borrow ("this call could return NULL");
514 }
515 return malloc_diagnostic::describe_state_change (change);
516 }
517
518 label_text describe_return_of_state (const evdesc::return_of_state &info)
519 FINAL OVERRIDE
520 {
521 if (unchecked_p (info.m_state))
522 return info.formatted_print ("possible return of NULL to %qE from %qE",
523 info.m_caller_fndecl, info.m_callee_fndecl);
524 return label_text ();
525 }
526
527 protected:
528 diagnostic_event_id_t m_origin_of_unchecked_event;
529 };
530
531 /* Concrete subclass for describing dereference of a possible NULL
532 value. */
533
534 class possible_null_deref : public possible_null
535 {
536 public:
537 possible_null_deref (const malloc_state_machine &sm, tree arg)
538 : possible_null (sm, arg)
539 {}
540
541 const char *get_kind () const FINAL OVERRIDE { return "possible_null_deref"; }
542
543 bool emit (rich_location *rich_loc) FINAL OVERRIDE
544 {
545 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
546 diagnostic_metadata m;
547 m.add_cwe (690);
548 return warning_meta (rich_loc, m,
549 OPT_Wanalyzer_possible_null_dereference,
550 "dereference of possibly-NULL %qE", m_arg);
551 }
552
553 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
554 {
555 if (m_origin_of_unchecked_event.known_p ())
556 return ev.formatted_print ("%qE could be NULL: unchecked value from %@",
557 ev.m_expr,
558 &m_origin_of_unchecked_event);
559 else
560 return ev.formatted_print ("%qE could be NULL", ev.m_expr);
561 }
562
563 };
564
565 /* Subroutine for use by possible_null_arg::emit and null_arg::emit.
566 Issue a note informing that the pertinent argument must be non-NULL. */
567
568 static void
569 inform_nonnull_attribute (tree fndecl, int arg_idx)
570 {
571 inform (DECL_SOURCE_LOCATION (fndecl),
572 "argument %u of %qD must be non-null",
573 arg_idx + 1, fndecl);
574 /* Ideally we would use the location of the parm and underline the
575 attribute also - but we don't have the location_t values at this point
576 in the middle-end.
577 For reference, the C and C++ FEs have get_fndecl_argument_location. */
578 }
579
580 /* Concrete subclass for describing passing a possibly-NULL value to a
581 function marked with __attribute__((nonnull)). */
582
583 class possible_null_arg : public possible_null
584 {
585 public:
586 possible_null_arg (const malloc_state_machine &sm, tree arg,
587 tree fndecl, int arg_idx)
588 : possible_null (sm, arg),
589 m_fndecl (fndecl), m_arg_idx (arg_idx)
590 {}
591
592 const char *get_kind () const FINAL OVERRIDE { return "possible_null_arg"; }
593
594 bool subclass_equal_p (const pending_diagnostic &base_other) const
595 {
596 const possible_null_arg &sub_other
597 = (const possible_null_arg &)base_other;
598 return (same_tree_p (m_arg, sub_other.m_arg)
599 && m_fndecl == sub_other.m_fndecl
600 && m_arg_idx == sub_other.m_arg_idx);
601 }
602
603
604 bool emit (rich_location *rich_loc) FINAL OVERRIDE
605 {
606 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
607 auto_diagnostic_group d;
608 diagnostic_metadata m;
609 m.add_cwe (690);
610 bool warned
611 = warning_meta (rich_loc, m, OPT_Wanalyzer_possible_null_argument,
612 "use of possibly-NULL %qE where non-null expected",
613 m_arg);
614 if (warned)
615 inform_nonnull_attribute (m_fndecl, m_arg_idx);
616 return warned;
617 }
618
619 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
620 {
621 if (m_origin_of_unchecked_event.known_p ())
622 return ev.formatted_print ("argument %u (%qE) from %@ could be NULL"
623 " where non-null expected",
624 m_arg_idx + 1, ev.m_expr,
625 &m_origin_of_unchecked_event);
626 else
627 return ev.formatted_print ("argument %u (%qE) could be NULL"
628 " where non-null expected",
629 m_arg_idx + 1, ev.m_expr);
630 }
631
632 private:
633 tree m_fndecl;
634 int m_arg_idx;
635 };
636
637 /* Concrete subclass for describing a dereference of a NULL value. */
638
639 class null_deref : public malloc_diagnostic
640 {
641 public:
642 null_deref (const malloc_state_machine &sm, tree arg)
643 : malloc_diagnostic (sm, arg) {}
644
645 const char *get_kind () const FINAL OVERRIDE { return "null_deref"; }
646
647 bool emit (rich_location *rich_loc) FINAL OVERRIDE
648 {
649 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
650 diagnostic_metadata m;
651 m.add_cwe (690);
652 return warning_meta (rich_loc, m,
653 OPT_Wanalyzer_null_dereference,
654 "dereference of NULL %qE", m_arg);
655 }
656
657 label_text describe_return_of_state (const evdesc::return_of_state &info)
658 FINAL OVERRIDE
659 {
660 if (info.m_state == m_sm.m_null)
661 return info.formatted_print ("return of NULL to %qE from %qE",
662 info.m_caller_fndecl, info.m_callee_fndecl);
663 return label_text ();
664 }
665
666 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
667 {
668 return ev.formatted_print ("dereference of NULL %qE", ev.m_expr);
669 }
670 };
671
672 /* Concrete subclass for describing passing a NULL value to a
673 function marked with __attribute__((nonnull)). */
674
675 class null_arg : public malloc_diagnostic
676 {
677 public:
678 null_arg (const malloc_state_machine &sm, tree arg,
679 tree fndecl, int arg_idx)
680 : malloc_diagnostic (sm, arg),
681 m_fndecl (fndecl), m_arg_idx (arg_idx)
682 {}
683
684 const char *get_kind () const FINAL OVERRIDE { return "null_arg"; }
685
686 bool subclass_equal_p (const pending_diagnostic &base_other) const
687 {
688 const null_arg &sub_other
689 = (const null_arg &)base_other;
690 return (same_tree_p (m_arg, sub_other.m_arg)
691 && m_fndecl == sub_other.m_fndecl
692 && m_arg_idx == sub_other.m_arg_idx);
693 }
694
695 bool emit (rich_location *rich_loc) FINAL OVERRIDE
696 {
697 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
698 auto_diagnostic_group d;
699 diagnostic_metadata m;
700 m.add_cwe (690);
701
702 bool warned;
703 if (zerop (m_arg))
704 warned = warning_meta (rich_loc, m, OPT_Wanalyzer_null_argument,
705 "use of NULL where non-null expected");
706 else
707 warned = warning_meta (rich_loc, m, OPT_Wanalyzer_null_argument,
708 "use of NULL %qE where non-null expected",
709 m_arg);
710 if (warned)
711 inform_nonnull_attribute (m_fndecl, m_arg_idx);
712 return warned;
713 }
714
715 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
716 {
717 if (zerop (ev.m_expr))
718 return ev.formatted_print ("argument %u NULL where non-null expected",
719 m_arg_idx + 1);
720 else
721 return ev.formatted_print ("argument %u (%qE) NULL"
722 " where non-null expected",
723 m_arg_idx + 1, ev.m_expr);
724 }
725
726 private:
727 tree m_fndecl;
728 int m_arg_idx;
729 };
730
731 class use_after_free : public malloc_diagnostic
732 {
733 public:
734 use_after_free (const malloc_state_machine &sm, tree arg,
735 const api *a)
736 : malloc_diagnostic (sm, arg), m_api (a)
737 {}
738
739 const char *get_kind () const FINAL OVERRIDE { return "use_after_free"; }
740
741 bool emit (rich_location *rich_loc) FINAL OVERRIDE
742 {
743 /* CWE-416: Use After Free. */
744 diagnostic_metadata m;
745 m.add_cwe (416);
746 return warning_meta (rich_loc, m, OPT_Wanalyzer_use_after_free,
747 "use after %<%s%> of %qE",
748 m_api->m_dealloc_funcname, m_arg);
749 }
750
751 label_text describe_state_change (const evdesc::state_change &change)
752 FINAL OVERRIDE
753 {
754 if (freed_p (change.m_new_state))
755 {
756 m_free_event = change.m_event_id;
757 switch (m_api->m_wording)
758 {
759 default:
760 gcc_unreachable ();
761 case WORDING_FREED:
762 return label_text::borrow ("freed here");
763 case WORDING_DELETED:
764 return label_text::borrow ("deleted here");
765 }
766 }
767 return malloc_diagnostic::describe_state_change (change);
768 }
769
770 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
771 {
772 const char *funcname = m_api->m_dealloc_funcname;
773 if (m_free_event.known_p ())
774 switch (m_api->m_wording)
775 {
776 default:
777 gcc_unreachable ();
778 case WORDING_FREED:
779 return ev.formatted_print ("use after %<%s%> of %qE; freed at %@",
780 funcname, ev.m_expr, &m_free_event);
781 case WORDING_DELETED:
782 return ev.formatted_print ("use after %<%s%> of %qE; deleted at %@",
783 funcname, ev.m_expr, &m_free_event);
784 }
785 else
786 return ev.formatted_print ("use after %<%s%> of %qE",
787 funcname, ev.m_expr);
788 }
789
790 private:
791 diagnostic_event_id_t m_free_event;
792 const api *m_api;
793 };
794
795 class malloc_leak : public malloc_diagnostic
796 {
797 public:
798 malloc_leak (const malloc_state_machine &sm, tree arg)
799 : malloc_diagnostic (sm, arg) {}
800
801 const char *get_kind () const FINAL OVERRIDE { return "malloc_leak"; }
802
803 bool emit (rich_location *rich_loc) FINAL OVERRIDE
804 {
805 diagnostic_metadata m;
806 m.add_cwe (401);
807 if (m_arg)
808 return warning_meta (rich_loc, m, OPT_Wanalyzer_malloc_leak,
809 "leak of %qE", m_arg);
810 else
811 return warning_meta (rich_loc, m, OPT_Wanalyzer_malloc_leak,
812 "leak of %qs", "<unknown>");
813 }
814
815 label_text describe_state_change (const evdesc::state_change &change)
816 FINAL OVERRIDE
817 {
818 if (unchecked_p (change.m_new_state))
819 {
820 m_alloc_event = change.m_event_id;
821 return label_text::borrow ("allocated here");
822 }
823 return malloc_diagnostic::describe_state_change (change);
824 }
825
826 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
827 {
828 if (ev.m_expr)
829 {
830 if (m_alloc_event.known_p ())
831 return ev.formatted_print ("%qE leaks here; was allocated at %@",
832 ev.m_expr, &m_alloc_event);
833 else
834 return ev.formatted_print ("%qE leaks here", ev.m_expr);
835 }
836 else
837 {
838 if (m_alloc_event.known_p ())
839 return ev.formatted_print ("%qs leaks here; was allocated at %@",
840 "<unknown>", &m_alloc_event);
841 else
842 return ev.formatted_print ("%qs leaks here", "<unknown>");
843 }
844 }
845
846 private:
847 diagnostic_event_id_t m_alloc_event;
848 };
849
850 class free_of_non_heap : public malloc_diagnostic
851 {
852 public:
853 free_of_non_heap (const malloc_state_machine &sm, tree arg,
854 const char *funcname)
855 : malloc_diagnostic (sm, arg), m_funcname (funcname), m_kind (KIND_UNKNOWN)
856 {
857 }
858
859 const char *get_kind () const FINAL OVERRIDE { return "free_of_non_heap"; }
860
861 bool subclass_equal_p (const pending_diagnostic &base_other) const
862 FINAL OVERRIDE
863 {
864 const free_of_non_heap &other = (const free_of_non_heap &)base_other;
865 return (same_tree_p (m_arg, other.m_arg) && m_kind == other.m_kind);
866 }
867
868 bool emit (rich_location *rich_loc) FINAL OVERRIDE
869 {
870 auto_diagnostic_group d;
871 diagnostic_metadata m;
872 m.add_cwe (590); /* CWE-590: Free of Memory not on the Heap. */
873 switch (m_kind)
874 {
875 default:
876 gcc_unreachable ();
877 case KIND_UNKNOWN:
878 return warning_meta (rich_loc, m, OPT_Wanalyzer_free_of_non_heap,
879 "%<%s%> of %qE which points to memory"
880 " not on the heap",
881 m_funcname, m_arg);
882 break;
883 case KIND_ALLOCA:
884 return warning_meta (rich_loc, m, OPT_Wanalyzer_free_of_non_heap,
885 "%<%s%> of memory allocated on the stack by"
886 " %qs (%qE) will corrupt the heap",
887 m_funcname, "alloca", m_arg);
888 break;
889 }
890 }
891
892 label_text describe_state_change (const evdesc::state_change &change)
893 FINAL OVERRIDE
894 {
895 /* Attempt to reconstruct what kind of pointer it is.
896 (It seems neater for this to be a part of the state, though). */
897 if (TREE_CODE (change.m_expr) == SSA_NAME)
898 {
899 gimple *def_stmt = SSA_NAME_DEF_STMT (change.m_expr);
900 if (gcall *call = dyn_cast <gcall *> (def_stmt))
901 {
902 if (is_special_named_call_p (call, "alloca", 1)
903 || is_special_named_call_p (call, "__builtin_alloca", 1))
904 {
905 m_kind = KIND_ALLOCA;
906 return label_text::borrow
907 ("memory is allocated on the stack here");
908 }
909 }
910 }
911 return label_text::borrow ("pointer is from here");
912 }
913
914 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
915 {
916 return ev.formatted_print ("call to %qs here", m_funcname);
917 }
918
919 private:
920 enum kind
921 {
922 KIND_UNKNOWN,
923 KIND_ALLOCA
924 };
925 const char *m_funcname;
926 enum kind m_kind;
927 };
928
929 /* struct allocation_state : public state_machine::state. */
930
931 /* Implementation of state_machine::state::dump_to_pp vfunc
932 for allocation_state: append the API that this allocation is
933 associated with. */
934
935 void
936 allocation_state::dump_to_pp (pretty_printer *pp) const
937 {
938 state_machine::state::dump_to_pp (pp);
939 if (m_api)
940 pp_printf (pp, " (%s)", m_api->m_name);
941 }
942
943 /* Given a allocation_state for an api, get the "nonnull" state
944 for the corresponding allocator. */
945
946 const allocation_state *
947 allocation_state::get_nonnull () const
948 {
949 gcc_assert (m_api);
950 return as_a_allocation_state (m_api->m_nonnull);
951 }
952
953 /* malloc_state_machine's ctor. */
954
955 malloc_state_machine::malloc_state_machine (logger *logger)
956 : state_machine ("malloc", logger),
957 m_malloc (this, "malloc", "free", WORDING_FREED),
958 m_scalar_new (this, "new", "delete", WORDING_DELETED),
959 m_vector_new (this, "new[]", "delete[]", WORDING_DELETED)
960 {
961 gcc_assert (m_start->get_id () == 0);
962 m_null = add_state ("null", RS_FREED, NULL);
963 m_non_heap = add_state ("non-heap", RS_NON_HEAP, NULL);
964 m_stop = add_state ("stop", RS_STOP, NULL);
965 }
966
967 state_machine::state_t
968 malloc_state_machine::add_state (const char *name, enum resource_state rs,
969 const api *a)
970 {
971 return add_custom_state (new allocation_state (name, alloc_state_id (),
972 rs, a));
973 }
974
975 /* Implementation of state_machine::on_stmt vfunc for malloc_state_machine. */
976
977 bool
978 malloc_state_machine::on_stmt (sm_context *sm_ctxt,
979 const supernode *node,
980 const gimple *stmt) const
981 {
982 if (const gcall *call = dyn_cast <const gcall *> (stmt))
983 if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call))
984 {
985 if (is_named_call_p (callee_fndecl, "malloc", call, 1)
986 || is_named_call_p (callee_fndecl, "calloc", call, 2)
987 || is_std_named_call_p (callee_fndecl, "malloc", call, 1)
988 || is_std_named_call_p (callee_fndecl, "calloc", call, 2)
989 || is_named_call_p (callee_fndecl, "__builtin_malloc", call, 1)
990 || is_named_call_p (callee_fndecl, "__builtin_calloc", call, 2)
991 || is_named_call_p (callee_fndecl, "strdup", call, 1)
992 || is_named_call_p (callee_fndecl, "strndup", call, 2))
993 {
994 on_allocator_call (sm_ctxt, call, m_malloc);
995 return true;
996 }
997
998 if (is_named_call_p (callee_fndecl, "operator new", call, 1))
999 on_allocator_call (sm_ctxt, call, m_scalar_new);
1000 else if (is_named_call_p (callee_fndecl, "operator new []", call, 1))
1001 on_allocator_call (sm_ctxt, call, m_vector_new);
1002 else if (is_named_call_p (callee_fndecl, "operator delete", call, 1)
1003 || is_named_call_p (callee_fndecl, "operator delete", call, 2))
1004 {
1005 on_deallocator_call (sm_ctxt, node, call, m_scalar_new);
1006 return true;
1007 }
1008 else if (is_named_call_p (callee_fndecl, "operator delete []", call, 1))
1009 {
1010 on_deallocator_call (sm_ctxt, node, call, m_vector_new);
1011 return true;
1012 }
1013
1014 if (is_named_call_p (callee_fndecl, "alloca", call, 1)
1015 || is_named_call_p (callee_fndecl, "__builtin_alloca", call, 1))
1016 {
1017 tree lhs = gimple_call_lhs (call);
1018 if (lhs)
1019 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_non_heap);
1020 return true;
1021 }
1022
1023 if (is_named_call_p (callee_fndecl, "free", call, 1)
1024 || is_std_named_call_p (callee_fndecl, "free", call, 1)
1025 || is_named_call_p (callee_fndecl, "__builtin_free", call, 1))
1026 {
1027 on_deallocator_call (sm_ctxt, node, call, m_malloc);
1028 return true;
1029 }
1030
1031 /* Handle "__attribute__((nonnull))". */
1032 {
1033 tree fntype = TREE_TYPE (callee_fndecl);
1034 bitmap nonnull_args = get_nonnull_args (fntype);
1035 if (nonnull_args)
1036 {
1037 for (unsigned i = 0; i < gimple_call_num_args (stmt); i++)
1038 {
1039 tree arg = gimple_call_arg (stmt, i);
1040 if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
1041 continue;
1042 /* If we have a nonnull-args, and either all pointers, or just
1043 the specified pointers. */
1044 if (bitmap_empty_p (nonnull_args)
1045 || bitmap_bit_p (nonnull_args, i))
1046 {
1047 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1048 state_t state = sm_ctxt->get_state (stmt, arg);
1049 /* Can't use a switch as the states are non-const. */
1050 if (unchecked_p (state))
1051 {
1052 sm_ctxt->warn (node, stmt, arg,
1053 new possible_null_arg (*this, diag_arg,
1054 callee_fndecl,
1055 i));
1056 const allocation_state *astate
1057 = as_a_allocation_state (state);
1058 sm_ctxt->set_next_state (stmt, arg,
1059 astate->get_nonnull ());
1060 }
1061 else if (state == m_null)
1062 {
1063 sm_ctxt->warn (node, stmt, arg,
1064 new null_arg (*this, diag_arg,
1065 callee_fndecl, i));
1066 sm_ctxt->set_next_state (stmt, arg, m_stop);
1067 }
1068 }
1069 }
1070 BITMAP_FREE (nonnull_args);
1071 }
1072 }
1073 }
1074
1075 if (tree lhs = sm_ctxt->is_zero_assignment (stmt))
1076 if (any_pointer_p (lhs))
1077 on_zero_assignment (sm_ctxt, stmt,lhs);
1078
1079 /* If we have "LHS = &EXPR;" and EXPR is something other than a MEM_REF,
1080 transition LHS from start to non_heap.
1081 Doing it for ADDR_EXPR(MEM_REF()) is likely wrong, and can lead to
1082 unbounded chains of unmergeable sm-state on pointer arithmetic in loops
1083 when optimization is enabled. */
1084 if (const gassign *assign_stmt = dyn_cast <const gassign *> (stmt))
1085 {
1086 enum tree_code op = gimple_assign_rhs_code (assign_stmt);
1087 if (op == ADDR_EXPR)
1088 {
1089 tree lhs = gimple_assign_lhs (assign_stmt);
1090 if (lhs)
1091 {
1092 tree addr_expr = gimple_assign_rhs1 (assign_stmt);
1093 if (TREE_CODE (TREE_OPERAND (addr_expr, 0)) != MEM_REF)
1094 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_non_heap);
1095 }
1096 }
1097 }
1098
1099 /* Handle dereferences. */
1100 for (unsigned i = 0; i < gimple_num_ops (stmt); i++)
1101 {
1102 tree op = gimple_op (stmt, i);
1103 if (!op)
1104 continue;
1105 if (TREE_CODE (op) == COMPONENT_REF)
1106 op = TREE_OPERAND (op, 0);
1107
1108 if (TREE_CODE (op) == MEM_REF)
1109 {
1110 tree arg = TREE_OPERAND (op, 0);
1111 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1112
1113 state_t state = sm_ctxt->get_state (stmt, arg);
1114 if (unchecked_p (state))
1115 {
1116 sm_ctxt->warn (node, stmt, arg,
1117 new possible_null_deref (*this, diag_arg));
1118 const allocation_state *astate = as_a_allocation_state (state);
1119 sm_ctxt->set_next_state (stmt, arg, astate->get_nonnull ());
1120 }
1121 else if (state == m_null)
1122 {
1123 sm_ctxt->warn (node, stmt, arg,
1124 new null_deref (*this, diag_arg));
1125 sm_ctxt->set_next_state (stmt, arg, m_stop);
1126 }
1127 else if (freed_p (state))
1128 {
1129 const allocation_state *astate = as_a_allocation_state (state);
1130 sm_ctxt->warn (node, stmt, arg,
1131 new use_after_free (*this, diag_arg,
1132 astate->m_api));
1133 sm_ctxt->set_next_state (stmt, arg, m_stop);
1134 }
1135 }
1136 }
1137 return false;
1138 }
1139
1140 /* Handle a call to an allocator. */
1141
1142 void
1143 malloc_state_machine::on_allocator_call (sm_context *sm_ctxt,
1144 const gcall *call,
1145 const api &ap) const
1146 {
1147 tree lhs = gimple_call_lhs (call);
1148 if (lhs)
1149 {
1150 if (sm_ctxt->get_state (call, lhs) == m_start)
1151 sm_ctxt->set_next_state (call, lhs, ap.m_unchecked);
1152 }
1153 else
1154 {
1155 /* TODO: report leak. */
1156 }
1157 }
1158
1159 void
1160 malloc_state_machine::on_deallocator_call (sm_context *sm_ctxt,
1161 const supernode *node,
1162 const gcall *call,
1163 const api &ap) const
1164 {
1165 tree arg = gimple_call_arg (call, 0);
1166 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1167
1168 state_t state = sm_ctxt->get_state (call, arg);
1169
1170 /* start/unchecked/nonnull -> freed. */
1171 if (state == m_start)
1172 sm_ctxt->set_next_state (call, arg, ap.m_freed);
1173 else if (unchecked_p (state) || nonnull_p (state))
1174 {
1175 const allocation_state *astate = as_a_allocation_state (state);
1176
1177 if (astate->m_api != &ap)
1178 {
1179 /* Wrong allocator. */
1180 pending_diagnostic *d
1181 = new mismatching_deallocation (*this, diag_arg,
1182 astate->m_api, &ap);
1183 sm_ctxt->warn (node, call, arg, d);
1184 }
1185 sm_ctxt->set_next_state (call, arg, ap.m_freed);
1186 }
1187
1188 /* Keep state "null" as-is, rather than transitioning to "freed";
1189 we don't want to complain about double-free of NULL. */
1190
1191 else if (state == ap.m_freed)
1192 {
1193 /* freed -> stop, with warning. */
1194 sm_ctxt->warn (node, call, arg,
1195 new double_free (*this, diag_arg,
1196 ap.m_dealloc_funcname));
1197 sm_ctxt->set_next_state (call, arg, m_stop);
1198 }
1199 else if (state == m_non_heap)
1200 {
1201 /* non-heap -> stop, with warning. */
1202 sm_ctxt->warn (node, call, arg,
1203 new free_of_non_heap (*this, diag_arg,
1204 ap.m_dealloc_funcname));
1205 sm_ctxt->set_next_state (call, arg, m_stop);
1206 }
1207 }
1208
1209 /* Implementation of state_machine::on_phi vfunc for malloc_state_machine. */
1210
1211 void
1212 malloc_state_machine::on_phi (sm_context *sm_ctxt,
1213 const supernode *node ATTRIBUTE_UNUSED,
1214 const gphi *phi,
1215 tree rhs) const
1216 {
1217 if (zerop (rhs))
1218 {
1219 tree lhs = gimple_phi_result (phi);
1220 on_zero_assignment (sm_ctxt, phi, lhs);
1221 }
1222 }
1223
1224 /* Implementation of state_machine::on_condition vfunc for malloc_state_machine.
1225 Potentially transition state 'unchecked' to 'nonnull' or to 'null'. */
1226
1227 void
1228 malloc_state_machine::on_condition (sm_context *sm_ctxt,
1229 const supernode *node ATTRIBUTE_UNUSED,
1230 const gimple *stmt,
1231 tree lhs,
1232 enum tree_code op,
1233 tree rhs) const
1234 {
1235 if (!zerop (rhs))
1236 return;
1237
1238 if (!any_pointer_p (lhs))
1239 return;
1240 if (!any_pointer_p (rhs))
1241 return;
1242
1243 if (op == NE_EXPR)
1244 {
1245 log ("got 'ARG != 0' match");
1246 state_t s = sm_ctxt->get_state (stmt, lhs);
1247 if (unchecked_p (s))
1248 {
1249 const allocation_state *astate = as_a_allocation_state (s);
1250 sm_ctxt->set_next_state (stmt, lhs, astate->get_nonnull ());
1251 }
1252 }
1253 else if (op == EQ_EXPR)
1254 {
1255 log ("got 'ARG == 0' match");
1256 state_t s = sm_ctxt->get_state (stmt, lhs);
1257 if (unchecked_p (s))
1258 sm_ctxt->set_next_state (stmt, lhs, m_null);
1259 }
1260 }
1261
1262 /* Implementation of state_machine::can_purge_p vfunc for malloc_state_machine.
1263 Don't allow purging of pointers in state 'unchecked' or 'nonnull'
1264 (to avoid false leak reports). */
1265
1266 bool
1267 malloc_state_machine::can_purge_p (state_t s) const
1268 {
1269 enum resource_state rs = get_rs (s);
1270 return rs != RS_UNCHECKED && rs != RS_NONNULL;
1271 }
1272
1273 /* Implementation of state_machine::on_leak vfunc for malloc_state_machine
1274 (for complaining about leaks of pointers in state 'unchecked' and
1275 'nonnull'). */
1276
1277 pending_diagnostic *
1278 malloc_state_machine::on_leak (tree var) const
1279 {
1280 return new malloc_leak (*this, var);
1281 }
1282
1283 /* Implementation of state_machine::reset_when_passed_to_unknown_fn_p vfunc
1284 for malloc_state_machine. */
1285
1286 bool
1287 malloc_state_machine::reset_when_passed_to_unknown_fn_p (state_t s,
1288 bool is_mutable) const
1289 {
1290 /* An on-stack ptr doesn't stop being stack-allocated when passed to an
1291 unknown fn. */
1292 if (s == m_non_heap)
1293 return false;
1294
1295 /* Otherwise, pointers passed as non-const can be freed. */
1296 return is_mutable;
1297 }
1298
1299 /* Shared logic for handling GIMPLE_ASSIGNs and GIMPLE_PHIs that
1300 assign zero to LHS. */
1301
1302 void
1303 malloc_state_machine::on_zero_assignment (sm_context *sm_ctxt,
1304 const gimple *stmt,
1305 tree lhs) const
1306 {
1307 state_t s = sm_ctxt->get_state (stmt, lhs);
1308 enum resource_state rs = get_rs (s);
1309 if (rs == RS_START
1310 || rs == RS_UNCHECKED
1311 || rs == RS_NONNULL
1312 || rs == RS_FREED)
1313 sm_ctxt->set_next_state (stmt, lhs, m_null);
1314 }
1315
1316 } // anonymous namespace
1317
1318 /* Internal interface to this file. */
1319
1320 state_machine *
1321 make_malloc_state_machine (logger *logger)
1322 {
1323 return new malloc_state_machine (logger);
1324 }
1325
1326 } // namespace ana
1327
1328 #endif /* #if ENABLE_ANALYZER */