analyzer: make summarized dumps more comprehensive
[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 "analyzer/analyzer.h"
34 #include "diagnostic-event-id.h"
35 #include "analyzer/analyzer-logging.h"
36 #include "analyzer/sm.h"
37 #include "analyzer/pending-diagnostic.h"
38
39 #if ENABLE_ANALYZER
40
41 namespace ana {
42
43 namespace {
44
45 /* A state machine for detecting misuses of the malloc/free API.
46
47 See sm-malloc.dot for an overview (keep this in-sync with that file). */
48
49 class malloc_state_machine : public state_machine
50 {
51 public:
52 malloc_state_machine (logger *logger);
53
54 bool inherited_state_p () const FINAL OVERRIDE { return false; }
55
56 bool on_stmt (sm_context *sm_ctxt,
57 const supernode *node,
58 const gimple *stmt) const FINAL OVERRIDE;
59
60 void on_phi (sm_context *sm_ctxt,
61 const supernode *node,
62 const gphi *phi,
63 tree rhs) const FINAL OVERRIDE;
64
65 void on_condition (sm_context *sm_ctxt,
66 const supernode *node,
67 const gimple *stmt,
68 tree lhs,
69 enum tree_code op,
70 tree rhs) const FINAL OVERRIDE;
71
72 bool can_purge_p (state_t s) const FINAL OVERRIDE;
73 pending_diagnostic *on_leak (tree var) const FINAL OVERRIDE;
74
75 /* Start state. */
76 state_t m_start;
77
78 /* State for a pointer returned from malloc that hasn't been checked for
79 NULL.
80 It could be a pointer to heap-allocated memory, or could be NULL. */
81 state_t m_unchecked;
82
83 /* State for a pointer that's known to be NULL. */
84 state_t m_null;
85
86 /* State for a pointer to heap-allocated memory, known to be non-NULL. */
87 state_t m_nonnull;
88
89 /* State for a pointer to freed memory. */
90 state_t m_freed;
91
92 /* State for a pointer that's known to not be on the heap (e.g. to a local
93 or global). */
94 state_t m_non_heap; // TODO: or should this be a different state machine?
95 // or do we need child values etc?
96
97 /* Stop state, for pointers we don't want to track any more. */
98 state_t m_stop;
99
100 private:
101 void on_zero_assignment (sm_context *sm_ctxt,
102 const supernode *node,
103 const gimple *stmt,
104 tree lhs) const;
105 };
106
107 /* Class for diagnostics relating to malloc_state_machine. */
108
109 class malloc_diagnostic : public pending_diagnostic
110 {
111 public:
112 malloc_diagnostic (const malloc_state_machine &sm, tree arg)
113 : m_sm (sm), m_arg (arg)
114 {}
115
116 bool subclass_equal_p (const pending_diagnostic &base_other) const OVERRIDE
117 {
118 return same_tree_p (m_arg, ((const malloc_diagnostic &)base_other).m_arg);
119 }
120
121 label_text describe_state_change (const evdesc::state_change &change)
122 OVERRIDE
123 {
124 if (change.m_old_state == m_sm.m_start
125 && change.m_new_state == m_sm.m_unchecked)
126 // TODO: verify that it's the allocation stmt, not a copy
127 return label_text::borrow ("allocated here");
128 if (change.m_old_state == m_sm.m_unchecked
129 && change.m_new_state == m_sm.m_nonnull)
130 return change.formatted_print ("assuming %qE is non-NULL",
131 change.m_expr);
132 if (change.m_new_state == m_sm.m_null)
133 {
134 if (change.m_old_state == m_sm.m_unchecked)
135 return change.formatted_print ("assuming %qE is NULL",
136 change.m_expr);
137 else
138 return change.formatted_print ("%qE is NULL",
139 change.m_expr);
140 }
141
142 return label_text ();
143 }
144
145 protected:
146 const malloc_state_machine &m_sm;
147 tree m_arg;
148 };
149
150 /* Concrete subclass for reporting double-free diagnostics. */
151
152 class double_free : public malloc_diagnostic
153 {
154 public:
155 double_free (const malloc_state_machine &sm, tree arg)
156 : malloc_diagnostic (sm, arg)
157 {}
158
159 const char *get_kind () const FINAL OVERRIDE { return "double_free"; }
160
161 bool emit (rich_location *rich_loc) FINAL OVERRIDE
162 {
163 auto_diagnostic_group d;
164 diagnostic_metadata m;
165 m.add_cwe (415); /* CWE-415: Double Free. */
166 return warning_meta (rich_loc, m, OPT_Wanalyzer_double_free,
167 "double-%<free%> of %qE", m_arg);
168 }
169
170 label_text describe_state_change (const evdesc::state_change &change)
171 FINAL OVERRIDE
172 {
173 if (change.m_new_state == m_sm.m_freed)
174 {
175 m_first_free_event = change.m_event_id;
176 return change.formatted_print ("first %qs here", "free");
177 }
178 return malloc_diagnostic::describe_state_change (change);
179 }
180
181 label_text describe_call_with_state (const evdesc::call_with_state &info)
182 FINAL OVERRIDE
183 {
184 if (info.m_state == m_sm.m_freed)
185 return info.formatted_print
186 ("passing freed pointer %qE in call to %qE from %qE",
187 info.m_expr, info.m_callee_fndecl, info.m_caller_fndecl);
188 return label_text ();
189 }
190
191 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
192 {
193 if (m_first_free_event.known_p ())
194 return ev.formatted_print ("second %qs here; first %qs was at %@",
195 "free", "free",
196 &m_first_free_event);
197 return ev.formatted_print ("second %qs here", "free");
198 }
199
200 private:
201 diagnostic_event_id_t m_first_free_event;
202 };
203
204 /* Abstract subclass for describing possible bad uses of NULL.
205 Responsible for describing the call that could return NULL. */
206
207 class possible_null : public malloc_diagnostic
208 {
209 public:
210 possible_null (const malloc_state_machine &sm, tree arg)
211 : malloc_diagnostic (sm, arg)
212 {}
213
214 label_text describe_state_change (const evdesc::state_change &change)
215 FINAL OVERRIDE
216 {
217 if (change.m_old_state == m_sm.m_start
218 && change.m_new_state == m_sm.m_unchecked)
219 {
220 m_origin_of_unchecked_event = change.m_event_id;
221 return label_text::borrow ("this call could return NULL");
222 }
223 return malloc_diagnostic::describe_state_change (change);
224 }
225
226 label_text describe_return_of_state (const evdesc::return_of_state &info)
227 FINAL OVERRIDE
228 {
229 if (info.m_state == m_sm.m_unchecked)
230 return info.formatted_print ("possible return of NULL to %qE from %qE",
231 info.m_caller_fndecl, info.m_callee_fndecl);
232 return label_text ();
233 }
234
235 protected:
236 diagnostic_event_id_t m_origin_of_unchecked_event;
237 };
238
239 /* Concrete subclass for describing dereference of a possible NULL
240 value. */
241
242 class possible_null_deref : public possible_null
243 {
244 public:
245 possible_null_deref (const malloc_state_machine &sm, tree arg)
246 : possible_null (sm, arg)
247 {}
248
249 const char *get_kind () const FINAL OVERRIDE { return "possible_null_deref"; }
250
251 bool emit (rich_location *rich_loc) FINAL OVERRIDE
252 {
253 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
254 diagnostic_metadata m;
255 m.add_cwe (690);
256 return warning_meta (rich_loc, m,
257 OPT_Wanalyzer_possible_null_dereference,
258 "dereference of possibly-NULL %qE", m_arg);
259 }
260
261 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
262 {
263 if (m_origin_of_unchecked_event.known_p ())
264 return ev.formatted_print ("%qE could be NULL: unchecked value from %@",
265 ev.m_expr,
266 &m_origin_of_unchecked_event);
267 else
268 return ev.formatted_print ("%qE could be NULL", ev.m_expr);
269 }
270
271 };
272
273 /* Subroutine for use by possible_null_arg::emit and null_arg::emit.
274 Issue a note informing that the pertinent argument must be non-NULL. */
275
276 static void
277 inform_nonnull_attribute (tree fndecl, int arg_idx)
278 {
279 inform (DECL_SOURCE_LOCATION (fndecl),
280 "argument %u of %qD must be non-null",
281 arg_idx + 1, fndecl);
282 /* Ideally we would use the location of the parm and underline the
283 attribute also - but we don't have the location_t values at this point
284 in the middle-end.
285 For reference, the C and C++ FEs have get_fndecl_argument_location. */
286 }
287
288 /* Concrete subclass for describing passing a possibly-NULL value to a
289 function marked with __attribute__((nonnull)). */
290
291 class possible_null_arg : public possible_null
292 {
293 public:
294 possible_null_arg (const malloc_state_machine &sm, tree arg,
295 tree fndecl, int arg_idx)
296 : possible_null (sm, arg),
297 m_fndecl (fndecl), m_arg_idx (arg_idx)
298 {}
299
300 const char *get_kind () const FINAL OVERRIDE { return "possible_null_arg"; }
301
302 bool subclass_equal_p (const pending_diagnostic &base_other) const
303 {
304 const possible_null_arg &sub_other
305 = (const possible_null_arg &)base_other;
306 return (same_tree_p (m_arg, sub_other.m_arg)
307 && m_fndecl == sub_other.m_fndecl
308 && m_arg_idx == sub_other.m_arg_idx);
309 }
310
311
312 bool emit (rich_location *rich_loc) FINAL OVERRIDE
313 {
314 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
315 auto_diagnostic_group d;
316 diagnostic_metadata m;
317 m.add_cwe (690);
318 bool warned
319 = warning_meta (rich_loc, m, OPT_Wanalyzer_possible_null_argument,
320 "use of possibly-NULL %qE where non-null expected",
321 m_arg);
322 if (warned)
323 inform_nonnull_attribute (m_fndecl, m_arg_idx);
324 return warned;
325 }
326
327 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
328 {
329 if (m_origin_of_unchecked_event.known_p ())
330 return ev.formatted_print ("argument %u (%qE) from %@ could be NULL"
331 " where non-null expected",
332 m_arg_idx + 1, ev.m_expr,
333 &m_origin_of_unchecked_event);
334 else
335 return ev.formatted_print ("argument %u (%qE) could be NULL"
336 " where non-null expected",
337 m_arg_idx + 1, ev.m_expr);
338 }
339
340 private:
341 tree m_fndecl;
342 int m_arg_idx;
343 };
344
345 /* Concrete subclass for describing a dereference of a NULL value. */
346
347 class null_deref : public malloc_diagnostic
348 {
349 public:
350 null_deref (const malloc_state_machine &sm, tree arg)
351 : malloc_diagnostic (sm, arg) {}
352
353 const char *get_kind () const FINAL OVERRIDE { return "null_deref"; }
354
355 bool emit (rich_location *rich_loc) FINAL OVERRIDE
356 {
357 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
358 diagnostic_metadata m;
359 m.add_cwe (690);
360 return warning_meta (rich_loc, m,
361 OPT_Wanalyzer_null_dereference,
362 "dereference of NULL %qE", m_arg);
363 }
364
365 label_text describe_return_of_state (const evdesc::return_of_state &info)
366 FINAL OVERRIDE
367 {
368 if (info.m_state == m_sm.m_null)
369 return info.formatted_print ("return of NULL to %qE from %qE",
370 info.m_caller_fndecl, info.m_callee_fndecl);
371 return label_text ();
372 }
373
374 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
375 {
376 return ev.formatted_print ("dereference of NULL %qE", ev.m_expr);
377 }
378 };
379
380 /* Concrete subclass for describing passing a NULL value to a
381 function marked with __attribute__((nonnull)). */
382
383 class null_arg : public malloc_diagnostic
384 {
385 public:
386 null_arg (const malloc_state_machine &sm, tree arg,
387 tree fndecl, int arg_idx)
388 : malloc_diagnostic (sm, arg),
389 m_fndecl (fndecl), m_arg_idx (arg_idx)
390 {}
391
392 const char *get_kind () const FINAL OVERRIDE { return "null_arg"; }
393
394 bool subclass_equal_p (const pending_diagnostic &base_other) const
395 {
396 const null_arg &sub_other
397 = (const null_arg &)base_other;
398 return (same_tree_p (m_arg, sub_other.m_arg)
399 && m_fndecl == sub_other.m_fndecl
400 && m_arg_idx == sub_other.m_arg_idx);
401 }
402
403 bool emit (rich_location *rich_loc) FINAL OVERRIDE
404 {
405 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
406 auto_diagnostic_group d;
407 diagnostic_metadata m;
408 m.add_cwe (690);
409 bool warned = warning_meta (rich_loc, m, OPT_Wanalyzer_null_argument,
410 "use of NULL %qE where non-null expected",
411 m_arg);
412 if (warned)
413 inform_nonnull_attribute (m_fndecl, m_arg_idx);
414 return warned;
415 }
416
417 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
418 {
419 return ev.formatted_print ("argument %u (%qE) NULL"
420 " where non-null expected",
421 m_arg_idx + 1, ev.m_expr);
422 }
423
424 private:
425 tree m_fndecl;
426 int m_arg_idx;
427 };
428
429 class use_after_free : public malloc_diagnostic
430 {
431 public:
432 use_after_free (const malloc_state_machine &sm, tree arg)
433 : malloc_diagnostic (sm, arg)
434 {}
435
436 const char *get_kind () const FINAL OVERRIDE { return "use_after_free"; }
437
438 bool emit (rich_location *rich_loc) FINAL OVERRIDE
439 {
440 /* CWE-416: Use After Free. */
441 diagnostic_metadata m;
442 m.add_cwe (416);
443 return warning_meta (rich_loc, m, OPT_Wanalyzer_use_after_free,
444 "use after %<free%> of %qE", m_arg);
445 }
446
447 label_text describe_state_change (const evdesc::state_change &change)
448 FINAL OVERRIDE
449 {
450 if (change.m_new_state == m_sm.m_freed)
451 {
452 m_free_event = change.m_event_id;
453 return label_text::borrow ("freed here");
454 }
455 return malloc_diagnostic::describe_state_change (change);
456 }
457
458 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
459 {
460 if (m_free_event.known_p ())
461 return ev.formatted_print ("use after %<free%> of %qE; freed at %@",
462 ev.m_expr, &m_free_event);
463 else
464 return ev.formatted_print ("use after %<free%> of %qE", ev.m_expr);
465 }
466
467 private:
468 diagnostic_event_id_t m_free_event;
469 };
470
471 class malloc_leak : public malloc_diagnostic
472 {
473 public:
474 malloc_leak (const malloc_state_machine &sm, tree arg)
475 : malloc_diagnostic (sm, arg) {}
476
477 const char *get_kind () const FINAL OVERRIDE { return "malloc_leak"; }
478
479 bool emit (rich_location *rich_loc) FINAL OVERRIDE
480 {
481 diagnostic_metadata m;
482 m.add_cwe (401);
483 return warning_meta (rich_loc, m, OPT_Wanalyzer_malloc_leak,
484 "leak of %qE", m_arg);
485 }
486
487 label_text describe_state_change (const evdesc::state_change &change)
488 FINAL OVERRIDE
489 {
490 if (change.m_new_state == m_sm.m_unchecked)
491 {
492 m_malloc_event = change.m_event_id;
493 return label_text::borrow ("allocated here");
494 }
495 return malloc_diagnostic::describe_state_change (change);
496 }
497
498 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
499 {
500 if (m_malloc_event.known_p ())
501 return ev.formatted_print ("%qE leaks here; was allocated at %@",
502 ev.m_expr, &m_malloc_event);
503 else
504 return ev.formatted_print ("%qE leaks here", ev.m_expr);
505 }
506
507 private:
508 diagnostic_event_id_t m_malloc_event;
509 };
510
511 class free_of_non_heap : public malloc_diagnostic
512 {
513 public:
514 free_of_non_heap (const malloc_state_machine &sm, tree arg)
515 : malloc_diagnostic (sm, arg), m_kind (KIND_UNKNOWN)
516 {
517 }
518
519 const char *get_kind () const FINAL OVERRIDE { return "free_of_non_heap"; }
520
521 bool subclass_equal_p (const pending_diagnostic &base_other) const
522 FINAL OVERRIDE
523 {
524 const free_of_non_heap &other = (const free_of_non_heap &)base_other;
525 return (same_tree_p (m_arg, other.m_arg) && m_kind == other.m_kind);
526 }
527
528 bool emit (rich_location *rich_loc) FINAL OVERRIDE
529 {
530 auto_diagnostic_group d;
531 diagnostic_metadata m;
532 m.add_cwe (590); /* CWE-590: Free of Memory not on the Heap. */
533 switch (m_kind)
534 {
535 default:
536 gcc_unreachable ();
537 case KIND_UNKNOWN:
538 return warning_meta (rich_loc, m, OPT_Wanalyzer_free_of_non_heap,
539 "%<free%> of %qE which points to memory"
540 " not on the heap",
541 m_arg);
542 break;
543 case KIND_ALLOCA:
544 return warning_meta (rich_loc, m, OPT_Wanalyzer_free_of_non_heap,
545 "%<free%> of memory allocated on the stack by"
546 " %qs (%qE) will corrupt the heap",
547 "alloca", m_arg);
548 break;
549 }
550 }
551
552 label_text describe_state_change (const evdesc::state_change &change)
553 FINAL OVERRIDE
554 {
555 /* Attempt to reconstruct what kind of pointer it is.
556 (It seems neater for this to be a part of the state, though). */
557 if (TREE_CODE (change.m_expr) == SSA_NAME)
558 {
559 gimple *def_stmt = SSA_NAME_DEF_STMT (change.m_expr);
560 if (gcall *call = dyn_cast <gcall *> (def_stmt))
561 {
562 if (is_special_named_call_p (call, "alloca", 1)
563 || is_special_named_call_p (call, "__builtin_alloca", 1))
564 {
565 m_kind = KIND_ALLOCA;
566 return label_text::borrow
567 ("memory is allocated on the stack here");
568 }
569 }
570 }
571 return label_text::borrow ("pointer is from here");
572 }
573
574 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
575 {
576 return ev.formatted_print ("call to %qs here", "free");
577 }
578
579 private:
580 enum kind
581 {
582 KIND_UNKNOWN,
583 KIND_ALLOCA
584 };
585 enum kind m_kind;
586 };
587
588 /* malloc_state_machine's ctor. */
589
590 malloc_state_machine::malloc_state_machine (logger *logger)
591 : state_machine ("malloc", logger)
592 {
593 m_start = add_state ("start");
594 m_unchecked = add_state ("unchecked");
595 m_null = add_state ("null");
596 m_nonnull = add_state ("nonnull");
597 m_freed = add_state ("freed");
598 m_non_heap = add_state ("non-heap");
599 m_stop = add_state ("stop");
600 }
601
602 /* Implementation of state_machine::on_stmt vfunc for malloc_state_machine. */
603
604 bool
605 malloc_state_machine::on_stmt (sm_context *sm_ctxt,
606 const supernode *node,
607 const gimple *stmt) const
608 {
609 if (const gcall *call = dyn_cast <const gcall *> (stmt))
610 if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call))
611 {
612 if (is_named_call_p (callee_fndecl, "malloc", call, 1)
613 || is_named_call_p (callee_fndecl, "calloc", call, 2)
614 || is_std_named_call_p (callee_fndecl, "malloc", call, 1)
615 || is_std_named_call_p (callee_fndecl, "calloc", call, 2)
616 || is_named_call_p (callee_fndecl, "__builtin_malloc", call, 1)
617 || is_named_call_p (callee_fndecl, "__builtin_calloc", call, 2))
618 {
619 tree lhs = gimple_call_lhs (call);
620 if (lhs)
621 {
622 lhs = sm_ctxt->get_readable_tree (lhs);
623 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_unchecked);
624 }
625 else
626 {
627 /* TODO: report leak. */
628 }
629 return true;
630 }
631
632 if (is_named_call_p (callee_fndecl, "alloca", call, 1)
633 || is_named_call_p (callee_fndecl, "__builtin_alloca", call, 1))
634 {
635 tree lhs = gimple_call_lhs (call);
636 if (lhs)
637 {
638 lhs = sm_ctxt->get_readable_tree (lhs);
639 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_non_heap);
640 }
641 return true;
642 }
643
644 if (is_named_call_p (callee_fndecl, "free", call, 1)
645 || is_std_named_call_p (callee_fndecl, "free", call, 1)
646 || is_named_call_p (callee_fndecl, "__builtin_free", call, 1))
647 {
648 tree arg = gimple_call_arg (call, 0);
649
650 arg = sm_ctxt->get_readable_tree (arg);
651
652 /* start/unchecked/nonnull -> freed. */
653 sm_ctxt->on_transition (node, stmt, arg, m_start, m_freed);
654 sm_ctxt->on_transition (node, stmt, arg, m_unchecked, m_freed);
655 sm_ctxt->on_transition (node, stmt, arg, m_nonnull, m_freed);
656
657 /* Keep state "null" as-is, rather than transitioning to "free";
658 we don't want to complain about double-free of NULL. */
659
660 /* freed -> stop, with warning. */
661 sm_ctxt->warn_for_state (node, stmt, arg, m_freed,
662 new double_free (*this, arg));
663 sm_ctxt->on_transition (node, stmt, arg, m_freed, m_stop);
664
665 /* non-heap -> stop, with warning. */
666 sm_ctxt->warn_for_state (node, stmt, arg, m_non_heap,
667 new free_of_non_heap (*this, arg));
668 sm_ctxt->on_transition (node, stmt, arg, m_non_heap, m_stop);
669 return true;
670 }
671
672 /* Handle "__attribute__((nonnull))". */
673 {
674 tree fntype = TREE_TYPE (callee_fndecl);
675 bitmap nonnull_args = get_nonnull_args (fntype);
676 if (nonnull_args)
677 {
678 for (unsigned i = 0; i < gimple_call_num_args (stmt); i++)
679 {
680 tree arg = gimple_call_arg (stmt, i);
681 if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
682 continue;
683 /* If we have a nonnull-args, and either all pointers, or just
684 the specified pointers. */
685 if (bitmap_empty_p (nonnull_args)
686 || bitmap_bit_p (nonnull_args, i))
687 {
688 sm_ctxt->warn_for_state
689 (node, stmt, arg, m_unchecked,
690 new possible_null_arg (*this, arg, callee_fndecl, i));
691 sm_ctxt->on_transition (node, stmt, arg, m_unchecked,
692 m_nonnull);
693
694 sm_ctxt->warn_for_state
695 (node, stmt, arg, m_null,
696 new null_arg (*this, arg, callee_fndecl, i));
697 sm_ctxt->on_transition (node, stmt, arg, m_null, m_stop);
698 }
699 }
700 BITMAP_FREE (nonnull_args);
701 }
702 }
703 }
704
705 if (tree lhs = is_zero_assignment (stmt))
706 if (any_pointer_p (lhs))
707 on_zero_assignment (sm_ctxt, node, stmt,lhs);
708
709 if (const gassign *assign_stmt = dyn_cast <const gassign *> (stmt))
710 {
711 enum tree_code op = gimple_assign_rhs_code (assign_stmt);
712 if (op == ADDR_EXPR)
713 {
714 tree lhs = gimple_assign_lhs (assign_stmt);
715 if (lhs)
716 {
717 lhs = sm_ctxt->get_readable_tree (lhs);
718 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_non_heap);
719 }
720 }
721 }
722
723 /* Handle dereferences. */
724 for (unsigned i = 0; i < gimple_num_ops (stmt); i++)
725 {
726 tree op = gimple_op (stmt, i);
727 if (!op)
728 continue;
729 if (TREE_CODE (op) == COMPONENT_REF)
730 op = TREE_OPERAND (op, 0);
731
732 if (TREE_CODE (op) == MEM_REF)
733 {
734 tree arg = TREE_OPERAND (op, 0);
735 arg = sm_ctxt->get_readable_tree (arg);
736
737 sm_ctxt->warn_for_state (node, stmt, arg, m_unchecked,
738 new possible_null_deref (*this, arg));
739 sm_ctxt->on_transition (node, stmt, arg, m_unchecked, m_nonnull);
740
741 sm_ctxt->warn_for_state (node, stmt, arg, m_null,
742 new null_deref (*this, arg));
743 sm_ctxt->on_transition (node, stmt, arg, m_null, m_stop);
744
745 sm_ctxt->warn_for_state (node, stmt, arg, m_freed,
746 new use_after_free (*this, arg));
747 sm_ctxt->on_transition (node, stmt, arg, m_freed, m_stop);
748 }
749 }
750 return false;
751 }
752
753 /* Implementation of state_machine::on_phi vfunc for malloc_state_machine. */
754
755 void
756 malloc_state_machine::on_phi (sm_context *sm_ctxt,
757 const supernode *node,
758 const gphi *phi,
759 tree rhs) const
760 {
761 if (zerop (rhs))
762 {
763 tree lhs = gimple_phi_result (phi);
764 on_zero_assignment (sm_ctxt, node, phi, lhs);
765 }
766 }
767
768 /* Implementation of state_machine::on_condition vfunc for malloc_state_machine.
769 Potentially transition state 'unchecked' to 'nonnull' or to 'null'. */
770
771 void
772 malloc_state_machine::on_condition (sm_context *sm_ctxt,
773 const supernode *node,
774 const gimple *stmt,
775 tree lhs,
776 enum tree_code op,
777 tree rhs) const
778 {
779 if (!zerop (rhs))
780 return;
781
782 if (!any_pointer_p (lhs))
783 return;
784 if (!any_pointer_p (rhs))
785 return;
786
787 if (op == NE_EXPR)
788 {
789 log ("got 'ARG != 0' match");
790 sm_ctxt->on_transition (node, stmt,
791 lhs, m_unchecked, m_nonnull);
792 }
793 else if (op == EQ_EXPR)
794 {
795 log ("got 'ARG == 0' match");
796 sm_ctxt->on_transition (node, stmt,
797 lhs, m_unchecked, m_null);
798 }
799 }
800
801 /* Implementation of state_machine::can_purge_p vfunc for malloc_state_machine.
802 Don't allow purging of pointers in state 'unchecked' or 'nonnull'
803 (to avoid false leak reports). */
804
805 bool
806 malloc_state_machine::can_purge_p (state_t s) const
807 {
808 return s != m_unchecked && s != m_nonnull;
809 }
810
811 /* Implementation of state_machine::on_leak vfunc for malloc_state_machine
812 (for complaining about leaks of pointers in state 'unchecked' and
813 'nonnull'). */
814
815 pending_diagnostic *
816 malloc_state_machine::on_leak (tree var) const
817 {
818 return new malloc_leak (*this, var);
819 }
820
821 /* Shared logic for handling GIMPLE_ASSIGNs and GIMPLE_PHIs that
822 assign zero to LHS. */
823
824 void
825 malloc_state_machine::on_zero_assignment (sm_context *sm_ctxt,
826 const supernode *node,
827 const gimple *stmt,
828 tree lhs) const
829 {
830 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_null);
831 sm_ctxt->on_transition (node, stmt, lhs, m_unchecked, m_null);
832 sm_ctxt->on_transition (node, stmt, lhs, m_nonnull, m_null);
833 sm_ctxt->on_transition (node, stmt, lhs, m_freed, m_null);
834 }
835
836 } // anonymous namespace
837
838 /* Internal interface to this file. */
839
840 state_machine *
841 make_malloc_state_machine (logger *logger)
842 {
843 return new malloc_state_machine (logger);
844 }
845
846 } // namespace ana
847
848 #endif /* #if ENABLE_ANALYZER */