]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/analyzer/sm-malloc.cc
diagnostic_metadata: unbreak xgettext (v2)
[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_meta (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_meta (rich_loc, m,
239 OPT_Wanalyzer_possible_null_dereference,
240 "dereference of possibly-NULL %qE", m_arg);
241 }
242
243 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
244 {
245 if (m_origin_of_unchecked_event.known_p ())
246 return ev.formatted_print ("%qE could be NULL: unchecked value from %@",
247 ev.m_expr,
248 &m_origin_of_unchecked_event);
249 else
250 return ev.formatted_print ("%qE could be NULL", ev.m_expr);
251 }
252
253 };
254
255 /* Subroutine for use by possible_null_arg::emit and null_arg::emit.
256 Issue a note informing that the pertinent argument must be non-NULL. */
257
258 static void
259 inform_nonnull_attribute (tree fndecl, int arg_idx)
260 {
261 inform (DECL_SOURCE_LOCATION (fndecl),
262 "argument %u of %qD must be non-null",
263 arg_idx + 1, fndecl);
264 /* Ideally we would use the location of the parm and underline the
265 attribute also - but we don't have the location_t values at this point
266 in the middle-end.
267 For reference, the C and C++ FEs have get_fndecl_argument_location. */
268 }
269
270 /* Concrete subclass for describing passing a possibly-NULL value to a
271 function marked with __attribute__((nonnull)). */
272
273 class possible_null_arg : public possible_null
274 {
275 public:
276 possible_null_arg (const malloc_state_machine &sm, tree arg,
277 tree fndecl, int arg_idx)
278 : possible_null (sm, arg),
279 m_fndecl (fndecl), m_arg_idx (arg_idx)
280 {}
281
282 const char *get_kind () const FINAL OVERRIDE { return "possible_null_arg"; }
283
284 bool subclass_equal_p (const pending_diagnostic &base_other) const
285 {
286 const possible_null_arg &sub_other
287 = (const possible_null_arg &)base_other;
288 return (same_tree_p (m_arg, sub_other.m_arg)
289 && m_fndecl == sub_other.m_fndecl
290 && m_arg_idx == sub_other.m_arg_idx);
291 }
292
293
294 bool emit (rich_location *rich_loc) FINAL OVERRIDE
295 {
296 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
297 auto_diagnostic_group d;
298 diagnostic_metadata m;
299 m.add_cwe (690);
300 bool warned
301 = warning_meta (rich_loc, m, OPT_Wanalyzer_possible_null_argument,
302 "use of possibly-NULL %qE where non-null expected",
303 m_arg);
304 if (warned)
305 inform_nonnull_attribute (m_fndecl, m_arg_idx);
306 return warned;
307 }
308
309 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
310 {
311 if (m_origin_of_unchecked_event.known_p ())
312 return ev.formatted_print ("argument %u (%qE) from %@ could be NULL"
313 " where non-null expected",
314 m_arg_idx + 1, ev.m_expr,
315 &m_origin_of_unchecked_event);
316 else
317 return ev.formatted_print ("argument %u (%qE) could be NULL"
318 " where non-null expected",
319 m_arg_idx + 1, ev.m_expr);
320 }
321
322 private:
323 tree m_fndecl;
324 int m_arg_idx;
325 };
326
327 /* Concrete subclass for describing a dereference of a NULL value. */
328
329 class null_deref : public malloc_diagnostic
330 {
331 public:
332 null_deref (const malloc_state_machine &sm, tree arg)
333 : malloc_diagnostic (sm, arg) {}
334
335 const char *get_kind () const FINAL OVERRIDE { return "null_deref"; }
336
337 bool emit (rich_location *rich_loc) FINAL OVERRIDE
338 {
339 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
340 diagnostic_metadata m;
341 m.add_cwe (690);
342 return warning_meta (rich_loc, m,
343 OPT_Wanalyzer_null_dereference,
344 "dereference of NULL %qE", m_arg);
345 }
346
347 label_text describe_return_of_state (const evdesc::return_of_state &info)
348 FINAL OVERRIDE
349 {
350 if (info.m_state == m_sm.m_null)
351 return info.formatted_print ("return of NULL to %qE from %qE",
352 info.m_caller_fndecl, info.m_callee_fndecl);
353 return label_text ();
354 }
355
356 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
357 {
358 return ev.formatted_print ("dereference of NULL %qE", ev.m_expr);
359 }
360 };
361
362 /* Concrete subclass for describing passing a NULL value to a
363 function marked with __attribute__((nonnull)). */
364
365 class null_arg : public malloc_diagnostic
366 {
367 public:
368 null_arg (const malloc_state_machine &sm, tree arg,
369 tree fndecl, int arg_idx)
370 : malloc_diagnostic (sm, arg),
371 m_fndecl (fndecl), m_arg_idx (arg_idx)
372 {}
373
374 const char *get_kind () const FINAL OVERRIDE { return "null_arg"; }
375
376 bool subclass_equal_p (const pending_diagnostic &base_other) const
377 {
378 const null_arg &sub_other
379 = (const null_arg &)base_other;
380 return (same_tree_p (m_arg, sub_other.m_arg)
381 && m_fndecl == sub_other.m_fndecl
382 && m_arg_idx == sub_other.m_arg_idx);
383 }
384
385 bool emit (rich_location *rich_loc) FINAL OVERRIDE
386 {
387 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
388 auto_diagnostic_group d;
389 diagnostic_metadata m;
390 m.add_cwe (690);
391 bool warned = warning_meta (rich_loc, m, OPT_Wanalyzer_null_argument,
392 "use of NULL %qE where non-null expected",
393 m_arg);
394 if (warned)
395 inform_nonnull_attribute (m_fndecl, m_arg_idx);
396 return warned;
397 }
398
399 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
400 {
401 return ev.formatted_print ("argument %u (%qE) NULL"
402 " where non-null expected",
403 m_arg_idx + 1, ev.m_expr);
404 }
405
406 private:
407 tree m_fndecl;
408 int m_arg_idx;
409 };
410
411 class use_after_free : public malloc_diagnostic
412 {
413 public:
414 use_after_free (const malloc_state_machine &sm, tree arg)
415 : malloc_diagnostic (sm, arg)
416 {}
417
418 const char *get_kind () const FINAL OVERRIDE { return "use_after_free"; }
419
420 bool emit (rich_location *rich_loc) FINAL OVERRIDE
421 {
422 /* CWE-416: Use After Free. */
423 diagnostic_metadata m;
424 m.add_cwe (416);
425 return warning_meta (rich_loc, m, OPT_Wanalyzer_use_after_free,
426 "use after %<free%> of %qE", m_arg);
427 }
428
429 label_text describe_state_change (const evdesc::state_change &change)
430 FINAL OVERRIDE
431 {
432 if (change.m_new_state == m_sm.m_freed)
433 {
434 m_free_event = change.m_event_id;
435 return label_text::borrow ("freed here");
436 }
437 return malloc_diagnostic::describe_state_change (change);
438 }
439
440 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
441 {
442 if (m_free_event.known_p ())
443 return ev.formatted_print ("use after %<free%> of %qE; freed at %@",
444 ev.m_expr, &m_free_event);
445 else
446 return ev.formatted_print ("use after %<free%> of %qE", ev.m_expr);
447 }
448
449 private:
450 diagnostic_event_id_t m_free_event;
451 };
452
453 class malloc_leak : public malloc_diagnostic
454 {
455 public:
456 malloc_leak (const malloc_state_machine &sm, tree arg)
457 : malloc_diagnostic (sm, arg) {}
458
459 const char *get_kind () const FINAL OVERRIDE { return "malloc_leak"; }
460
461 bool emit (rich_location *rich_loc) FINAL OVERRIDE
462 {
463 diagnostic_metadata m;
464 m.add_cwe (401);
465 return warning_meta (rich_loc, m, OPT_Wanalyzer_malloc_leak,
466 "leak of %qE", m_arg);
467 }
468
469 label_text describe_state_change (const evdesc::state_change &change)
470 FINAL OVERRIDE
471 {
472 if (change.m_new_state == m_sm.m_unchecked)
473 {
474 m_malloc_event = change.m_event_id;
475 return label_text::borrow ("allocated here");
476 }
477 return malloc_diagnostic::describe_state_change (change);
478 }
479
480 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
481 {
482 if (m_malloc_event.known_p ())
483 return ev.formatted_print ("%qE leaks here; was allocated at %@",
484 ev.m_expr, &m_malloc_event);
485 else
486 return ev.formatted_print ("%qE leaks here", ev.m_expr);
487 }
488
489 private:
490 diagnostic_event_id_t m_malloc_event;
491 };
492
493 class free_of_non_heap : public malloc_diagnostic
494 {
495 public:
496 free_of_non_heap (const malloc_state_machine &sm, tree arg)
497 : malloc_diagnostic (sm, arg), m_kind (KIND_UNKNOWN)
498 {
499 }
500
501 const char *get_kind () const FINAL OVERRIDE { return "free_of_non_heap"; }
502
503 bool subclass_equal_p (const pending_diagnostic &base_other) const
504 FINAL OVERRIDE
505 {
506 const free_of_non_heap &other = (const free_of_non_heap &)base_other;
507 return (same_tree_p (m_arg, other.m_arg) && m_kind == other.m_kind);
508 }
509
510 bool emit (rich_location *rich_loc) FINAL OVERRIDE
511 {
512 auto_diagnostic_group d;
513 diagnostic_metadata m;
514 m.add_cwe (590); /* CWE-590: Free of Memory not on the Heap. */
515 switch (m_kind)
516 {
517 default:
518 gcc_unreachable ();
519 case KIND_UNKNOWN:
520 return warning_meta (rich_loc, m, OPT_Wanalyzer_free_of_non_heap,
521 "%<free%> of %qE which points to memory"
522 " not on the heap",
523 m_arg);
524 break;
525 case KIND_ALLOCA:
526 return warning_meta (rich_loc, m, OPT_Wanalyzer_free_of_non_heap,
527 "%<free%> of memory allocated on the stack by"
528 " %qs (%qE) will corrupt the heap",
529 "alloca", m_arg);
530 break;
531 }
532 }
533
534 label_text describe_state_change (const evdesc::state_change &change)
535 FINAL OVERRIDE
536 {
537 /* Attempt to reconstruct what kind of pointer it is.
538 (It seems neater for this to be a part of the state, though). */
539 if (TREE_CODE (change.m_expr) == SSA_NAME)
540 {
541 gimple *def_stmt = SSA_NAME_DEF_STMT (change.m_expr);
542 if (gcall *call = dyn_cast <gcall *> (def_stmt))
543 {
544 if (is_special_named_call_p (call, "alloca", 1)
545 || is_special_named_call_p (call, "__builtin_alloca", 1))
546 {
547 m_kind = KIND_ALLOCA;
548 return label_text::borrow
549 ("memory is allocated on the stack here");
550 }
551 }
552 }
553 return label_text::borrow ("pointer is from here");
554 }
555
556 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
557 {
558 return ev.formatted_print ("call to %qs here", "free");
559 }
560
561 private:
562 enum kind
563 {
564 KIND_UNKNOWN,
565 KIND_ALLOCA
566 };
567 enum kind m_kind;
568 };
569
570 /* malloc_state_machine's ctor. */
571
572 malloc_state_machine::malloc_state_machine (logger *logger)
573 : state_machine ("malloc", logger)
574 {
575 m_start = add_state ("start");
576 m_unchecked = add_state ("unchecked");
577 m_null = add_state ("null");
578 m_nonnull = add_state ("nonnull");
579 m_freed = add_state ("freed");
580 m_non_heap = add_state ("non-heap");
581 m_stop = add_state ("stop");
582 }
583
584 /* Implementation of state_machine::on_stmt vfunc for malloc_state_machine. */
585
586 bool
587 malloc_state_machine::on_stmt (sm_context *sm_ctxt,
588 const supernode *node,
589 const gimple *stmt) const
590 {
591 if (const gcall *call = dyn_cast <const gcall *> (stmt))
592 if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call))
593 {
594 if (is_named_call_p (callee_fndecl, "malloc", call, 1)
595 || is_named_call_p (callee_fndecl, "calloc", call, 2)
596 || is_named_call_p (callee_fndecl, "__builtin_malloc", call, 1)
597 || is_named_call_p (callee_fndecl, "__builtin_calloc", call, 2))
598 {
599 tree lhs = gimple_call_lhs (call);
600 if (lhs)
601 {
602 lhs = sm_ctxt->get_readable_tree (lhs);
603 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_unchecked);
604 }
605 else
606 {
607 /* TODO: report leak. */
608 }
609 return true;
610 }
611
612 if (is_named_call_p (callee_fndecl, "alloca", call, 1)
613 || is_named_call_p (callee_fndecl, "__builtin_alloca", call, 1))
614 {
615 tree lhs = gimple_call_lhs (call);
616 if (lhs)
617 {
618 lhs = sm_ctxt->get_readable_tree (lhs);
619 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_non_heap);
620 }
621 return true;
622 }
623
624 if (is_named_call_p (callee_fndecl, "free", call, 1)
625 || is_named_call_p (callee_fndecl, "__builtin_free", call, 1))
626 {
627 tree arg = gimple_call_arg (call, 0);
628
629 arg = sm_ctxt->get_readable_tree (arg);
630
631 /* start/unchecked/nonnull -> freed. */
632 sm_ctxt->on_transition (node, stmt, arg, m_start, m_freed);
633 sm_ctxt->on_transition (node, stmt, arg, m_unchecked, m_freed);
634 sm_ctxt->on_transition (node, stmt, arg, m_nonnull, m_freed);
635
636 /* Keep state "null" as-is, rather than transitioning to "free";
637 we don't want want to complain about double-free of NULL. */
638
639 /* freed -> stop, with warning. */
640 sm_ctxt->warn_for_state (node, stmt, arg, m_freed,
641 new double_free (*this, arg));
642 sm_ctxt->on_transition (node, stmt, arg, m_freed, m_stop);
643
644 /* non-heap -> stop, with warning. */
645 sm_ctxt->warn_for_state (node, stmt, arg, m_non_heap,
646 new free_of_non_heap (*this, arg));
647 sm_ctxt->on_transition (node, stmt, arg, m_non_heap, m_stop);
648 return true;
649 }
650
651 /* Handle "__attribute__((nonnull))". */
652 {
653 tree fntype = TREE_TYPE (callee_fndecl);
654 bitmap nonnull_args = get_nonnull_args (fntype);
655 if (nonnull_args)
656 {
657 for (unsigned i = 0; i < gimple_call_num_args (stmt); i++)
658 {
659 tree arg = gimple_call_arg (stmt, i);
660 if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
661 continue;
662 /* If we have a nonnull-args, and either all pointers, or just
663 the specified pointers. */
664 if (bitmap_empty_p (nonnull_args)
665 || bitmap_bit_p (nonnull_args, i))
666 {
667 sm_ctxt->warn_for_state
668 (node, stmt, arg, m_unchecked,
669 new possible_null_arg (*this, arg, callee_fndecl, i));
670 sm_ctxt->on_transition (node, stmt, arg, m_unchecked,
671 m_nonnull);
672
673 sm_ctxt->warn_for_state
674 (node, stmt, arg, m_null,
675 new null_arg (*this, arg, callee_fndecl, i));
676 sm_ctxt->on_transition (node, stmt, arg, m_null, m_stop);
677 }
678 }
679 BITMAP_FREE (nonnull_args);
680 }
681 }
682 }
683
684 if (tree lhs = is_zero_assignment (stmt))
685 {
686 if (any_pointer_p (lhs))
687 {
688 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_null);
689 sm_ctxt->on_transition (node, stmt, lhs, m_unchecked, m_null);
690 sm_ctxt->on_transition (node, stmt, lhs, m_nonnull, m_null);
691 sm_ctxt->on_transition (node, stmt, lhs, m_freed, m_null);
692 }
693 }
694
695 if (const gassign *assign_stmt = dyn_cast <const gassign *> (stmt))
696 {
697 enum tree_code op = gimple_assign_rhs_code (assign_stmt);
698 if (op == ADDR_EXPR)
699 {
700 tree lhs = gimple_assign_lhs (assign_stmt);
701 if (lhs)
702 {
703 lhs = sm_ctxt->get_readable_tree (lhs);
704 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_non_heap);
705 }
706 }
707 }
708
709 /* Handle dereferences. */
710 for (unsigned i = 0; i < gimple_num_ops (stmt); i++)
711 {
712 tree op = gimple_op (stmt, i);
713 if (!op)
714 continue;
715 if (TREE_CODE (op) == COMPONENT_REF)
716 op = TREE_OPERAND (op, 0);
717
718 if (TREE_CODE (op) == MEM_REF)
719 {
720 tree arg = TREE_OPERAND (op, 0);
721 arg = sm_ctxt->get_readable_tree (arg);
722
723 sm_ctxt->warn_for_state (node, stmt, arg, m_unchecked,
724 new possible_null_deref (*this, arg));
725 sm_ctxt->on_transition (node, stmt, arg, m_unchecked, m_nonnull);
726
727 sm_ctxt->warn_for_state (node, stmt, arg, m_null,
728 new null_deref (*this, arg));
729 sm_ctxt->on_transition (node, stmt, arg, m_null, m_stop);
730
731 sm_ctxt->warn_for_state (node, stmt, arg, m_freed,
732 new use_after_free (*this, arg));
733 sm_ctxt->on_transition (node, stmt, arg, m_freed, m_stop);
734 }
735 }
736 return false;
737 }
738
739 /* Implementation of state_machine::on_condition vfunc for malloc_state_machine.
740 Potentially transition state 'unchecked' to 'nonnull' or to 'null'. */
741
742 void
743 malloc_state_machine::on_condition (sm_context *sm_ctxt,
744 const supernode *node,
745 const gimple *stmt,
746 tree lhs,
747 enum tree_code op,
748 tree rhs) const
749 {
750 if (!zerop (rhs))
751 return;
752
753 if (!any_pointer_p (lhs))
754 return;
755 if (!any_pointer_p (rhs))
756 return;
757
758 if (op == NE_EXPR)
759 {
760 log ("got 'ARG != 0' match");
761 sm_ctxt->on_transition (node, stmt,
762 lhs, m_unchecked, m_nonnull);
763 }
764 else if (op == EQ_EXPR)
765 {
766 log ("got 'ARG == 0' match");
767 sm_ctxt->on_transition (node, stmt,
768 lhs, m_unchecked, m_null);
769 }
770 }
771
772 /* Implementation of state_machine::can_purge_p vfunc for malloc_state_machine.
773 Don't allow purging of pointers in state 'unchecked' or 'nonnull'
774 (to avoid false leak reports). */
775
776 bool
777 malloc_state_machine::can_purge_p (state_t s) const
778 {
779 return s != m_unchecked && s != m_nonnull;
780 }
781
782 /* Implementation of state_machine::on_leak vfunc for malloc_state_machine
783 (for complaining about leaks of pointers in state 'unchecked' and
784 'nonnull'). */
785
786 pending_diagnostic *
787 malloc_state_machine::on_leak (tree var) const
788 {
789 return new malloc_leak (*this, var);
790 }
791
792 } // anonymous namespace
793
794 /* Internal interface to this file. */
795
796 state_machine *
797 make_malloc_state_machine (logger *logger)
798 {
799 return new malloc_state_machine (logger);
800 }
801
802 } // namespace ana
803
804 #endif /* #if ENABLE_ANALYZER */