]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/analyzer/sm-malloc.cc
analyzer: handle C++ argument numbers and "this" [PR97116]
[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 /* Return true if FNDECL is a C++ method. */
566
567 static bool
568 method_p (tree fndecl)
569 {
570 return TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE;
571 }
572
573 /* Return a 1-based description of ARG_IDX (0-based) of FNDECL.
574 Compare with %P in the C++ FE (implemented in cp/error.c: parm_to_string
575 as called from cp_printer). */
576
577 static label_text
578 describe_argument_index (tree fndecl, int arg_idx)
579 {
580 if (method_p (fndecl))
581 if (arg_idx == 0)
582 return label_text::borrow ("'this'");
583 pretty_printer pp;
584 pp_printf (&pp, "%u", arg_idx + 1 - method_p (fndecl));
585 return label_text::take (xstrdup (pp_formatted_text (&pp)));
586 }
587
588 /* Subroutine for use by possible_null_arg::emit and null_arg::emit.
589 Issue a note informing that the pertinent argument must be non-NULL. */
590
591 static void
592 inform_nonnull_attribute (tree fndecl, int arg_idx)
593 {
594 label_text arg_desc = describe_argument_index (fndecl, arg_idx);
595 inform (DECL_SOURCE_LOCATION (fndecl),
596 "argument %s of %qD must be non-null",
597 arg_desc.m_buffer, fndecl);
598 arg_desc.maybe_free ();
599 /* Ideally we would use the location of the parm and underline the
600 attribute also - but we don't have the location_t values at this point
601 in the middle-end.
602 For reference, the C and C++ FEs have get_fndecl_argument_location. */
603 }
604
605 /* Concrete subclass for describing passing a possibly-NULL value to a
606 function marked with __attribute__((nonnull)). */
607
608 class possible_null_arg : public possible_null
609 {
610 public:
611 possible_null_arg (const malloc_state_machine &sm, tree arg,
612 tree fndecl, int arg_idx)
613 : possible_null (sm, arg),
614 m_fndecl (fndecl), m_arg_idx (arg_idx)
615 {}
616
617 const char *get_kind () const FINAL OVERRIDE { return "possible_null_arg"; }
618
619 bool subclass_equal_p (const pending_diagnostic &base_other) const
620 {
621 const possible_null_arg &sub_other
622 = (const possible_null_arg &)base_other;
623 return (same_tree_p (m_arg, sub_other.m_arg)
624 && m_fndecl == sub_other.m_fndecl
625 && m_arg_idx == sub_other.m_arg_idx);
626 }
627
628
629 bool emit (rich_location *rich_loc) FINAL OVERRIDE
630 {
631 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
632 auto_diagnostic_group d;
633 diagnostic_metadata m;
634 m.add_cwe (690);
635 bool warned
636 = warning_meta (rich_loc, m, OPT_Wanalyzer_possible_null_argument,
637 "use of possibly-NULL %qE where non-null expected",
638 m_arg);
639 if (warned)
640 inform_nonnull_attribute (m_fndecl, m_arg_idx);
641 return warned;
642 }
643
644 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
645 {
646 label_text arg_desc = describe_argument_index (m_fndecl, m_arg_idx);
647 label_text result;
648 if (m_origin_of_unchecked_event.known_p ())
649 result = ev.formatted_print ("argument %s (%qE) from %@ could be NULL"
650 " where non-null expected",
651 arg_desc.m_buffer, ev.m_expr,
652 &m_origin_of_unchecked_event);
653 else
654 result = ev.formatted_print ("argument %s (%qE) could be NULL"
655 " where non-null expected",
656 arg_desc.m_buffer, ev.m_expr);
657 arg_desc.maybe_free ();
658 return result;
659 }
660
661 private:
662 tree m_fndecl;
663 int m_arg_idx;
664 };
665
666 /* Concrete subclass for describing a dereference of a NULL value. */
667
668 class null_deref : public malloc_diagnostic
669 {
670 public:
671 null_deref (const malloc_state_machine &sm, tree arg)
672 : malloc_diagnostic (sm, arg) {}
673
674 const char *get_kind () const FINAL OVERRIDE { return "null_deref"; }
675
676 bool emit (rich_location *rich_loc) FINAL OVERRIDE
677 {
678 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
679 diagnostic_metadata m;
680 m.add_cwe (690);
681 return warning_meta (rich_loc, m,
682 OPT_Wanalyzer_null_dereference,
683 "dereference of NULL %qE", m_arg);
684 }
685
686 label_text describe_return_of_state (const evdesc::return_of_state &info)
687 FINAL OVERRIDE
688 {
689 if (info.m_state == m_sm.m_null)
690 return info.formatted_print ("return of NULL to %qE from %qE",
691 info.m_caller_fndecl, info.m_callee_fndecl);
692 return label_text ();
693 }
694
695 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
696 {
697 return ev.formatted_print ("dereference of NULL %qE", ev.m_expr);
698 }
699 };
700
701 /* Concrete subclass for describing passing a NULL value to a
702 function marked with __attribute__((nonnull)). */
703
704 class null_arg : public malloc_diagnostic
705 {
706 public:
707 null_arg (const malloc_state_machine &sm, tree arg,
708 tree fndecl, int arg_idx)
709 : malloc_diagnostic (sm, arg),
710 m_fndecl (fndecl), m_arg_idx (arg_idx)
711 {}
712
713 const char *get_kind () const FINAL OVERRIDE { return "null_arg"; }
714
715 bool subclass_equal_p (const pending_diagnostic &base_other) const
716 {
717 const null_arg &sub_other
718 = (const null_arg &)base_other;
719 return (same_tree_p (m_arg, sub_other.m_arg)
720 && m_fndecl == sub_other.m_fndecl
721 && m_arg_idx == sub_other.m_arg_idx);
722 }
723
724 bool emit (rich_location *rich_loc) FINAL OVERRIDE
725 {
726 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
727 auto_diagnostic_group d;
728 diagnostic_metadata m;
729 m.add_cwe (690);
730
731 bool warned;
732 if (zerop (m_arg))
733 warned = warning_meta (rich_loc, m, OPT_Wanalyzer_null_argument,
734 "use of NULL where non-null expected");
735 else
736 warned = warning_meta (rich_loc, m, OPT_Wanalyzer_null_argument,
737 "use of NULL %qE where non-null expected",
738 m_arg);
739 if (warned)
740 inform_nonnull_attribute (m_fndecl, m_arg_idx);
741 return warned;
742 }
743
744 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
745 {
746 label_text arg_desc = describe_argument_index (m_fndecl, m_arg_idx);
747 label_text result;
748 if (zerop (ev.m_expr))
749 result = ev.formatted_print ("argument %s NULL where non-null expected",
750 arg_desc.m_buffer);
751 else
752 result = ev.formatted_print ("argument %s (%qE) NULL"
753 " where non-null expected",
754 arg_desc.m_buffer, ev.m_expr);
755 arg_desc.maybe_free ();
756 return result;
757 }
758
759 private:
760 tree m_fndecl;
761 int m_arg_idx;
762 };
763
764 class use_after_free : public malloc_diagnostic
765 {
766 public:
767 use_after_free (const malloc_state_machine &sm, tree arg,
768 const api *a)
769 : malloc_diagnostic (sm, arg), m_api (a)
770 {}
771
772 const char *get_kind () const FINAL OVERRIDE { return "use_after_free"; }
773
774 bool emit (rich_location *rich_loc) FINAL OVERRIDE
775 {
776 /* CWE-416: Use After Free. */
777 diagnostic_metadata m;
778 m.add_cwe (416);
779 return warning_meta (rich_loc, m, OPT_Wanalyzer_use_after_free,
780 "use after %<%s%> of %qE",
781 m_api->m_dealloc_funcname, m_arg);
782 }
783
784 label_text describe_state_change (const evdesc::state_change &change)
785 FINAL OVERRIDE
786 {
787 if (freed_p (change.m_new_state))
788 {
789 m_free_event = change.m_event_id;
790 switch (m_api->m_wording)
791 {
792 default:
793 gcc_unreachable ();
794 case WORDING_FREED:
795 return label_text::borrow ("freed here");
796 case WORDING_DELETED:
797 return label_text::borrow ("deleted here");
798 }
799 }
800 return malloc_diagnostic::describe_state_change (change);
801 }
802
803 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
804 {
805 const char *funcname = m_api->m_dealloc_funcname;
806 if (m_free_event.known_p ())
807 switch (m_api->m_wording)
808 {
809 default:
810 gcc_unreachable ();
811 case WORDING_FREED:
812 return ev.formatted_print ("use after %<%s%> of %qE; freed at %@",
813 funcname, ev.m_expr, &m_free_event);
814 case WORDING_DELETED:
815 return ev.formatted_print ("use after %<%s%> of %qE; deleted at %@",
816 funcname, ev.m_expr, &m_free_event);
817 }
818 else
819 return ev.formatted_print ("use after %<%s%> of %qE",
820 funcname, ev.m_expr);
821 }
822
823 private:
824 diagnostic_event_id_t m_free_event;
825 const api *m_api;
826 };
827
828 class malloc_leak : public malloc_diagnostic
829 {
830 public:
831 malloc_leak (const malloc_state_machine &sm, tree arg)
832 : malloc_diagnostic (sm, arg) {}
833
834 const char *get_kind () const FINAL OVERRIDE { return "malloc_leak"; }
835
836 bool emit (rich_location *rich_loc) FINAL OVERRIDE
837 {
838 diagnostic_metadata m;
839 m.add_cwe (401);
840 if (m_arg)
841 return warning_meta (rich_loc, m, OPT_Wanalyzer_malloc_leak,
842 "leak of %qE", m_arg);
843 else
844 return warning_meta (rich_loc, m, OPT_Wanalyzer_malloc_leak,
845 "leak of %qs", "<unknown>");
846 }
847
848 label_text describe_state_change (const evdesc::state_change &change)
849 FINAL OVERRIDE
850 {
851 if (unchecked_p (change.m_new_state))
852 {
853 m_alloc_event = change.m_event_id;
854 return label_text::borrow ("allocated here");
855 }
856 return malloc_diagnostic::describe_state_change (change);
857 }
858
859 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
860 {
861 if (ev.m_expr)
862 {
863 if (m_alloc_event.known_p ())
864 return ev.formatted_print ("%qE leaks here; was allocated at %@",
865 ev.m_expr, &m_alloc_event);
866 else
867 return ev.formatted_print ("%qE leaks here", ev.m_expr);
868 }
869 else
870 {
871 if (m_alloc_event.known_p ())
872 return ev.formatted_print ("%qs leaks here; was allocated at %@",
873 "<unknown>", &m_alloc_event);
874 else
875 return ev.formatted_print ("%qs leaks here", "<unknown>");
876 }
877 }
878
879 private:
880 diagnostic_event_id_t m_alloc_event;
881 };
882
883 class free_of_non_heap : public malloc_diagnostic
884 {
885 public:
886 free_of_non_heap (const malloc_state_machine &sm, tree arg,
887 const char *funcname)
888 : malloc_diagnostic (sm, arg), m_funcname (funcname), m_kind (KIND_UNKNOWN)
889 {
890 }
891
892 const char *get_kind () const FINAL OVERRIDE { return "free_of_non_heap"; }
893
894 bool subclass_equal_p (const pending_diagnostic &base_other) const
895 FINAL OVERRIDE
896 {
897 const free_of_non_heap &other = (const free_of_non_heap &)base_other;
898 return (same_tree_p (m_arg, other.m_arg) && m_kind == other.m_kind);
899 }
900
901 bool emit (rich_location *rich_loc) FINAL OVERRIDE
902 {
903 auto_diagnostic_group d;
904 diagnostic_metadata m;
905 m.add_cwe (590); /* CWE-590: Free of Memory not on the Heap. */
906 switch (m_kind)
907 {
908 default:
909 gcc_unreachable ();
910 case KIND_UNKNOWN:
911 return warning_meta (rich_loc, m, OPT_Wanalyzer_free_of_non_heap,
912 "%<%s%> of %qE which points to memory"
913 " not on the heap",
914 m_funcname, m_arg);
915 break;
916 case KIND_ALLOCA:
917 return warning_meta (rich_loc, m, OPT_Wanalyzer_free_of_non_heap,
918 "%<%s%> of memory allocated on the stack by"
919 " %qs (%qE) will corrupt the heap",
920 m_funcname, "alloca", m_arg);
921 break;
922 }
923 }
924
925 label_text describe_state_change (const evdesc::state_change &change)
926 FINAL OVERRIDE
927 {
928 /* Attempt to reconstruct what kind of pointer it is.
929 (It seems neater for this to be a part of the state, though). */
930 if (TREE_CODE (change.m_expr) == SSA_NAME)
931 {
932 gimple *def_stmt = SSA_NAME_DEF_STMT (change.m_expr);
933 if (gcall *call = dyn_cast <gcall *> (def_stmt))
934 {
935 if (is_special_named_call_p (call, "alloca", 1)
936 || is_special_named_call_p (call, "__builtin_alloca", 1))
937 {
938 m_kind = KIND_ALLOCA;
939 return label_text::borrow
940 ("memory is allocated on the stack here");
941 }
942 }
943 }
944 return label_text::borrow ("pointer is from here");
945 }
946
947 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
948 {
949 return ev.formatted_print ("call to %qs here", m_funcname);
950 }
951
952 private:
953 enum kind
954 {
955 KIND_UNKNOWN,
956 KIND_ALLOCA
957 };
958 const char *m_funcname;
959 enum kind m_kind;
960 };
961
962 /* struct allocation_state : public state_machine::state. */
963
964 /* Implementation of state_machine::state::dump_to_pp vfunc
965 for allocation_state: append the API that this allocation is
966 associated with. */
967
968 void
969 allocation_state::dump_to_pp (pretty_printer *pp) const
970 {
971 state_machine::state::dump_to_pp (pp);
972 if (m_api)
973 pp_printf (pp, " (%s)", m_api->m_name);
974 }
975
976 /* Given a allocation_state for an api, get the "nonnull" state
977 for the corresponding allocator. */
978
979 const allocation_state *
980 allocation_state::get_nonnull () const
981 {
982 gcc_assert (m_api);
983 return as_a_allocation_state (m_api->m_nonnull);
984 }
985
986 /* malloc_state_machine's ctor. */
987
988 malloc_state_machine::malloc_state_machine (logger *logger)
989 : state_machine ("malloc", logger),
990 m_malloc (this, "malloc", "free", WORDING_FREED),
991 m_scalar_new (this, "new", "delete", WORDING_DELETED),
992 m_vector_new (this, "new[]", "delete[]", WORDING_DELETED)
993 {
994 gcc_assert (m_start->get_id () == 0);
995 m_null = add_state ("null", RS_FREED, NULL);
996 m_non_heap = add_state ("non-heap", RS_NON_HEAP, NULL);
997 m_stop = add_state ("stop", RS_STOP, NULL);
998 }
999
1000 state_machine::state_t
1001 malloc_state_machine::add_state (const char *name, enum resource_state rs,
1002 const api *a)
1003 {
1004 return add_custom_state (new allocation_state (name, alloc_state_id (),
1005 rs, a));
1006 }
1007
1008 /* Implementation of state_machine::on_stmt vfunc for malloc_state_machine. */
1009
1010 bool
1011 malloc_state_machine::on_stmt (sm_context *sm_ctxt,
1012 const supernode *node,
1013 const gimple *stmt) const
1014 {
1015 if (const gcall *call = dyn_cast <const gcall *> (stmt))
1016 if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call))
1017 {
1018 if (is_named_call_p (callee_fndecl, "malloc", call, 1)
1019 || is_named_call_p (callee_fndecl, "calloc", call, 2)
1020 || is_std_named_call_p (callee_fndecl, "malloc", call, 1)
1021 || is_std_named_call_p (callee_fndecl, "calloc", call, 2)
1022 || is_named_call_p (callee_fndecl, "__builtin_malloc", call, 1)
1023 || is_named_call_p (callee_fndecl, "__builtin_calloc", call, 2)
1024 || is_named_call_p (callee_fndecl, "strdup", call, 1)
1025 || is_named_call_p (callee_fndecl, "strndup", call, 2))
1026 {
1027 on_allocator_call (sm_ctxt, call, m_malloc);
1028 return true;
1029 }
1030
1031 if (is_named_call_p (callee_fndecl, "operator new", call, 1))
1032 on_allocator_call (sm_ctxt, call, m_scalar_new);
1033 else if (is_named_call_p (callee_fndecl, "operator new []", call, 1))
1034 on_allocator_call (sm_ctxt, call, m_vector_new);
1035 else if (is_named_call_p (callee_fndecl, "operator delete", call, 1)
1036 || is_named_call_p (callee_fndecl, "operator delete", call, 2))
1037 {
1038 on_deallocator_call (sm_ctxt, node, call, m_scalar_new);
1039 return true;
1040 }
1041 else if (is_named_call_p (callee_fndecl, "operator delete []", call, 1))
1042 {
1043 on_deallocator_call (sm_ctxt, node, call, m_vector_new);
1044 return true;
1045 }
1046
1047 if (is_named_call_p (callee_fndecl, "alloca", call, 1)
1048 || is_named_call_p (callee_fndecl, "__builtin_alloca", call, 1))
1049 {
1050 tree lhs = gimple_call_lhs (call);
1051 if (lhs)
1052 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_non_heap);
1053 return true;
1054 }
1055
1056 if (is_named_call_p (callee_fndecl, "free", call, 1)
1057 || is_std_named_call_p (callee_fndecl, "free", call, 1)
1058 || is_named_call_p (callee_fndecl, "__builtin_free", call, 1))
1059 {
1060 on_deallocator_call (sm_ctxt, node, call, m_malloc);
1061 return true;
1062 }
1063
1064 /* Handle "__attribute__((nonnull))". */
1065 {
1066 tree fntype = TREE_TYPE (callee_fndecl);
1067 bitmap nonnull_args = get_nonnull_args (fntype);
1068 if (nonnull_args)
1069 {
1070 for (unsigned i = 0; i < gimple_call_num_args (stmt); i++)
1071 {
1072 tree arg = gimple_call_arg (stmt, i);
1073 if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
1074 continue;
1075 /* If we have a nonnull-args, and either all pointers, or just
1076 the specified pointers. */
1077 if (bitmap_empty_p (nonnull_args)
1078 || bitmap_bit_p (nonnull_args, i))
1079 {
1080 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1081 state_t state = sm_ctxt->get_state (stmt, arg);
1082 /* Can't use a switch as the states are non-const. */
1083 if (unchecked_p (state))
1084 {
1085 sm_ctxt->warn (node, stmt, arg,
1086 new possible_null_arg (*this, diag_arg,
1087 callee_fndecl,
1088 i));
1089 const allocation_state *astate
1090 = as_a_allocation_state (state);
1091 sm_ctxt->set_next_state (stmt, arg,
1092 astate->get_nonnull ());
1093 }
1094 else if (state == m_null)
1095 {
1096 sm_ctxt->warn (node, stmt, arg,
1097 new null_arg (*this, diag_arg,
1098 callee_fndecl, i));
1099 sm_ctxt->set_next_state (stmt, arg, m_stop);
1100 }
1101 }
1102 }
1103 BITMAP_FREE (nonnull_args);
1104 }
1105 }
1106 }
1107
1108 if (tree lhs = sm_ctxt->is_zero_assignment (stmt))
1109 if (any_pointer_p (lhs))
1110 on_zero_assignment (sm_ctxt, stmt,lhs);
1111
1112 /* If we have "LHS = &EXPR;" and EXPR is something other than a MEM_REF,
1113 transition LHS from start to non_heap.
1114 Doing it for ADDR_EXPR(MEM_REF()) is likely wrong, and can lead to
1115 unbounded chains of unmergeable sm-state on pointer arithmetic in loops
1116 when optimization is enabled. */
1117 if (const gassign *assign_stmt = dyn_cast <const gassign *> (stmt))
1118 {
1119 enum tree_code op = gimple_assign_rhs_code (assign_stmt);
1120 if (op == ADDR_EXPR)
1121 {
1122 tree lhs = gimple_assign_lhs (assign_stmt);
1123 if (lhs)
1124 {
1125 tree addr_expr = gimple_assign_rhs1 (assign_stmt);
1126 if (TREE_CODE (TREE_OPERAND (addr_expr, 0)) != MEM_REF)
1127 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_non_heap);
1128 }
1129 }
1130 }
1131
1132 /* Handle dereferences. */
1133 for (unsigned i = 0; i < gimple_num_ops (stmt); i++)
1134 {
1135 tree op = gimple_op (stmt, i);
1136 if (!op)
1137 continue;
1138 if (TREE_CODE (op) == COMPONENT_REF)
1139 op = TREE_OPERAND (op, 0);
1140
1141 if (TREE_CODE (op) == MEM_REF)
1142 {
1143 tree arg = TREE_OPERAND (op, 0);
1144 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1145
1146 state_t state = sm_ctxt->get_state (stmt, arg);
1147 if (unchecked_p (state))
1148 {
1149 sm_ctxt->warn (node, stmt, arg,
1150 new possible_null_deref (*this, diag_arg));
1151 const allocation_state *astate = as_a_allocation_state (state);
1152 sm_ctxt->set_next_state (stmt, arg, astate->get_nonnull ());
1153 }
1154 else if (state == m_null)
1155 {
1156 sm_ctxt->warn (node, stmt, arg,
1157 new null_deref (*this, diag_arg));
1158 sm_ctxt->set_next_state (stmt, arg, m_stop);
1159 }
1160 else if (freed_p (state))
1161 {
1162 const allocation_state *astate = as_a_allocation_state (state);
1163 sm_ctxt->warn (node, stmt, arg,
1164 new use_after_free (*this, diag_arg,
1165 astate->m_api));
1166 sm_ctxt->set_next_state (stmt, arg, m_stop);
1167 }
1168 }
1169 }
1170 return false;
1171 }
1172
1173 /* Handle a call to an allocator. */
1174
1175 void
1176 malloc_state_machine::on_allocator_call (sm_context *sm_ctxt,
1177 const gcall *call,
1178 const api &ap) const
1179 {
1180 tree lhs = gimple_call_lhs (call);
1181 if (lhs)
1182 {
1183 if (sm_ctxt->get_state (call, lhs) == m_start)
1184 sm_ctxt->set_next_state (call, lhs, ap.m_unchecked);
1185 }
1186 else
1187 {
1188 /* TODO: report leak. */
1189 }
1190 }
1191
1192 void
1193 malloc_state_machine::on_deallocator_call (sm_context *sm_ctxt,
1194 const supernode *node,
1195 const gcall *call,
1196 const api &ap) const
1197 {
1198 tree arg = gimple_call_arg (call, 0);
1199 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1200
1201 state_t state = sm_ctxt->get_state (call, arg);
1202
1203 /* start/unchecked/nonnull -> freed. */
1204 if (state == m_start)
1205 sm_ctxt->set_next_state (call, arg, ap.m_freed);
1206 else if (unchecked_p (state) || nonnull_p (state))
1207 {
1208 const allocation_state *astate = as_a_allocation_state (state);
1209
1210 if (astate->m_api != &ap)
1211 {
1212 /* Wrong allocator. */
1213 pending_diagnostic *d
1214 = new mismatching_deallocation (*this, diag_arg,
1215 astate->m_api, &ap);
1216 sm_ctxt->warn (node, call, arg, d);
1217 }
1218 sm_ctxt->set_next_state (call, arg, ap.m_freed);
1219 }
1220
1221 /* Keep state "null" as-is, rather than transitioning to "freed";
1222 we don't want to complain about double-free of NULL. */
1223
1224 else if (state == ap.m_freed)
1225 {
1226 /* freed -> stop, with warning. */
1227 sm_ctxt->warn (node, call, arg,
1228 new double_free (*this, diag_arg,
1229 ap.m_dealloc_funcname));
1230 sm_ctxt->set_next_state (call, arg, m_stop);
1231 }
1232 else if (state == m_non_heap)
1233 {
1234 /* non-heap -> stop, with warning. */
1235 sm_ctxt->warn (node, call, arg,
1236 new free_of_non_heap (*this, diag_arg,
1237 ap.m_dealloc_funcname));
1238 sm_ctxt->set_next_state (call, arg, m_stop);
1239 }
1240 }
1241
1242 /* Implementation of state_machine::on_phi vfunc for malloc_state_machine. */
1243
1244 void
1245 malloc_state_machine::on_phi (sm_context *sm_ctxt,
1246 const supernode *node ATTRIBUTE_UNUSED,
1247 const gphi *phi,
1248 tree rhs) const
1249 {
1250 if (zerop (rhs))
1251 {
1252 tree lhs = gimple_phi_result (phi);
1253 on_zero_assignment (sm_ctxt, phi, lhs);
1254 }
1255 }
1256
1257 /* Implementation of state_machine::on_condition vfunc for malloc_state_machine.
1258 Potentially transition state 'unchecked' to 'nonnull' or to 'null'. */
1259
1260 void
1261 malloc_state_machine::on_condition (sm_context *sm_ctxt,
1262 const supernode *node ATTRIBUTE_UNUSED,
1263 const gimple *stmt,
1264 tree lhs,
1265 enum tree_code op,
1266 tree rhs) const
1267 {
1268 if (!zerop (rhs))
1269 return;
1270
1271 if (!any_pointer_p (lhs))
1272 return;
1273 if (!any_pointer_p (rhs))
1274 return;
1275
1276 if (op == NE_EXPR)
1277 {
1278 log ("got 'ARG != 0' match");
1279 state_t s = sm_ctxt->get_state (stmt, lhs);
1280 if (unchecked_p (s))
1281 {
1282 const allocation_state *astate = as_a_allocation_state (s);
1283 sm_ctxt->set_next_state (stmt, lhs, astate->get_nonnull ());
1284 }
1285 }
1286 else if (op == EQ_EXPR)
1287 {
1288 log ("got 'ARG == 0' match");
1289 state_t s = sm_ctxt->get_state (stmt, lhs);
1290 if (unchecked_p (s))
1291 sm_ctxt->set_next_state (stmt, lhs, m_null);
1292 }
1293 }
1294
1295 /* Implementation of state_machine::can_purge_p vfunc for malloc_state_machine.
1296 Don't allow purging of pointers in state 'unchecked' or 'nonnull'
1297 (to avoid false leak reports). */
1298
1299 bool
1300 malloc_state_machine::can_purge_p (state_t s) const
1301 {
1302 enum resource_state rs = get_rs (s);
1303 return rs != RS_UNCHECKED && rs != RS_NONNULL;
1304 }
1305
1306 /* Implementation of state_machine::on_leak vfunc for malloc_state_machine
1307 (for complaining about leaks of pointers in state 'unchecked' and
1308 'nonnull'). */
1309
1310 pending_diagnostic *
1311 malloc_state_machine::on_leak (tree var) const
1312 {
1313 return new malloc_leak (*this, var);
1314 }
1315
1316 /* Implementation of state_machine::reset_when_passed_to_unknown_fn_p vfunc
1317 for malloc_state_machine. */
1318
1319 bool
1320 malloc_state_machine::reset_when_passed_to_unknown_fn_p (state_t s,
1321 bool is_mutable) const
1322 {
1323 /* An on-stack ptr doesn't stop being stack-allocated when passed to an
1324 unknown fn. */
1325 if (s == m_non_heap)
1326 return false;
1327
1328 /* Otherwise, pointers passed as non-const can be freed. */
1329 return is_mutable;
1330 }
1331
1332 /* Shared logic for handling GIMPLE_ASSIGNs and GIMPLE_PHIs that
1333 assign zero to LHS. */
1334
1335 void
1336 malloc_state_machine::on_zero_assignment (sm_context *sm_ctxt,
1337 const gimple *stmt,
1338 tree lhs) const
1339 {
1340 state_t s = sm_ctxt->get_state (stmt, lhs);
1341 enum resource_state rs = get_rs (s);
1342 if (rs == RS_START
1343 || rs == RS_UNCHECKED
1344 || rs == RS_NONNULL
1345 || rs == RS_FREED)
1346 sm_ctxt->set_next_state (stmt, lhs, m_null);
1347 }
1348
1349 } // anonymous namespace
1350
1351 /* Internal interface to this file. */
1352
1353 state_machine *
1354 make_malloc_state_machine (logger *logger)
1355 {
1356 return new malloc_state_machine (logger);
1357 }
1358
1359 } // namespace ana
1360
1361 #endif /* #if ENABLE_ANALYZER */