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