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>.
5 This file is part of GCC.
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)
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.
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/>. */
23 #include "coretypes.h"
26 #include "basic-block.h"
30 #include "diagnostic-path.h"
31 #include "diagnostic-metadata.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"
45 /* A state machine for detecting misuses of the malloc/free API.
47 See sm-malloc.dot for an overview (keep this in-sync with that file). */
49 class malloc_state_machine
: public state_machine
52 malloc_state_machine (logger
*logger
);
54 bool inherited_state_p () const FINAL OVERRIDE
{ return false; }
56 bool on_stmt (sm_context
*sm_ctxt
,
57 const supernode
*node
,
58 const gimple
*stmt
) const FINAL OVERRIDE
;
60 void on_phi (sm_context
*sm_ctxt
,
61 const supernode
*node
,
63 tree rhs
) const FINAL OVERRIDE
;
65 void on_condition (sm_context
*sm_ctxt
,
66 const supernode
*node
,
70 tree rhs
) const FINAL OVERRIDE
;
72 bool can_purge_p (state_t s
) const FINAL OVERRIDE
;
73 pending_diagnostic
*on_leak (tree var
) const FINAL OVERRIDE
;
78 /* State for a pointer returned from malloc that hasn't been checked for
80 It could be a pointer to heap-allocated memory, or could be NULL. */
83 /* State for a pointer that's known to be NULL. */
86 /* State for a pointer to heap-allocated memory, known to be non-NULL. */
89 /* State for a pointer to freed memory. */
92 /* State for a pointer that's known to not be on the heap (e.g. to a local
94 state_t m_non_heap
; // TODO: or should this be a different state machine?
95 // or do we need child values etc?
97 /* Stop state, for pointers we don't want to track any more. */
101 void on_zero_assignment (sm_context
*sm_ctxt
,
102 const supernode
*node
,
107 /* Class for diagnostics relating to malloc_state_machine. */
109 class malloc_diagnostic
: public pending_diagnostic
112 malloc_diagnostic (const malloc_state_machine
&sm
, tree arg
)
113 : m_sm (sm
), m_arg (arg
)
116 bool subclass_equal_p (const pending_diagnostic
&base_other
) const OVERRIDE
118 return same_tree_p (m_arg
, ((const malloc_diagnostic
&)base_other
).m_arg
);
121 label_text
describe_state_change (const evdesc::state_change
&change
)
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",
132 if (change
.m_new_state
== m_sm
.m_null
)
134 if (change
.m_old_state
== m_sm
.m_unchecked
)
135 return change
.formatted_print ("assuming %qE is NULL",
138 return change
.formatted_print ("%qE is NULL",
142 return label_text ();
146 const malloc_state_machine
&m_sm
;
150 /* Concrete subclass for reporting double-free diagnostics. */
152 class double_free
: public malloc_diagnostic
155 double_free (const malloc_state_machine
&sm
, tree arg
)
156 : malloc_diagnostic (sm
, arg
)
159 const char *get_kind () const FINAL OVERRIDE
{ return "double_free"; }
161 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
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
);
170 label_text
describe_state_change (const evdesc::state_change
&change
)
173 if (change
.m_new_state
== m_sm
.m_freed
)
175 m_first_free_event
= change
.m_event_id
;
176 return change
.formatted_print ("first %qs here", "free");
178 return malloc_diagnostic::describe_state_change (change
);
181 label_text
describe_call_with_state (const evdesc::call_with_state
&info
)
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 ();
191 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
193 if (m_first_free_event
.known_p ())
194 return ev
.formatted_print ("second %qs here; first %qs was at %@",
196 &m_first_free_event
);
197 return ev
.formatted_print ("second %qs here", "free");
201 diagnostic_event_id_t m_first_free_event
;
204 /* Abstract subclass for describing possible bad uses of NULL.
205 Responsible for describing the call that could return NULL. */
207 class possible_null
: public malloc_diagnostic
210 possible_null (const malloc_state_machine
&sm
, tree arg
)
211 : malloc_diagnostic (sm
, arg
)
214 label_text
describe_state_change (const evdesc::state_change
&change
)
217 if (change
.m_old_state
== m_sm
.m_start
218 && change
.m_new_state
== m_sm
.m_unchecked
)
220 m_origin_of_unchecked_event
= change
.m_event_id
;
221 return label_text::borrow ("this call could return NULL");
223 return malloc_diagnostic::describe_state_change (change
);
226 label_text
describe_return_of_state (const evdesc::return_of_state
&info
)
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 ();
236 diagnostic_event_id_t m_origin_of_unchecked_event
;
239 /* Concrete subclass for describing dereference of a possible NULL
242 class possible_null_deref
: public possible_null
245 possible_null_deref (const malloc_state_machine
&sm
, tree arg
)
246 : possible_null (sm
, arg
)
249 const char *get_kind () const FINAL OVERRIDE
{ return "possible_null_deref"; }
251 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
253 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
254 diagnostic_metadata m
;
256 return warning_meta (rich_loc
, m
,
257 OPT_Wanalyzer_possible_null_dereference
,
258 "dereference of possibly-NULL %qE", m_arg
);
261 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
263 if (m_origin_of_unchecked_event
.known_p ())
264 return ev
.formatted_print ("%qE could be NULL: unchecked value from %@",
266 &m_origin_of_unchecked_event
);
268 return ev
.formatted_print ("%qE could be NULL", ev
.m_expr
);
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. */
277 inform_nonnull_attribute (tree fndecl
, int arg_idx
)
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
285 For reference, the C and C++ FEs have get_fndecl_argument_location. */
288 /* Concrete subclass for describing passing a possibly-NULL value to a
289 function marked with __attribute__((nonnull)). */
291 class possible_null_arg
: public possible_null
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
)
300 const char *get_kind () const FINAL OVERRIDE
{ return "possible_null_arg"; }
302 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
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
);
312 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
314 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
315 auto_diagnostic_group d
;
316 diagnostic_metadata m
;
319 = warning_meta (rich_loc
, m
, OPT_Wanalyzer_possible_null_argument
,
320 "use of possibly-NULL %qE where non-null expected",
323 inform_nonnull_attribute (m_fndecl
, m_arg_idx
);
327 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
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
);
335 return ev
.formatted_print ("argument %u (%qE) could be NULL"
336 " where non-null expected",
337 m_arg_idx
+ 1, ev
.m_expr
);
345 /* Concrete subclass for describing a dereference of a NULL value. */
347 class null_deref
: public malloc_diagnostic
350 null_deref (const malloc_state_machine
&sm
, tree arg
)
351 : malloc_diagnostic (sm
, arg
) {}
353 const char *get_kind () const FINAL OVERRIDE
{ return "null_deref"; }
355 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
357 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
358 diagnostic_metadata m
;
360 return warning_meta (rich_loc
, m
,
361 OPT_Wanalyzer_null_dereference
,
362 "dereference of NULL %qE", m_arg
);
365 label_text
describe_return_of_state (const evdesc::return_of_state
&info
)
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 ();
374 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
376 return ev
.formatted_print ("dereference of NULL %qE", ev
.m_expr
);
380 /* Concrete subclass for describing passing a NULL value to a
381 function marked with __attribute__((nonnull)). */
383 class null_arg
: public malloc_diagnostic
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
)
392 const char *get_kind () const FINAL OVERRIDE
{ return "null_arg"; }
394 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
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
);
403 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
405 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
406 auto_diagnostic_group d
;
407 diagnostic_metadata m
;
409 bool warned
= warning_meta (rich_loc
, m
, OPT_Wanalyzer_null_argument
,
410 "use of NULL %qE where non-null expected",
413 inform_nonnull_attribute (m_fndecl
, m_arg_idx
);
417 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
419 return ev
.formatted_print ("argument %u (%qE) NULL"
420 " where non-null expected",
421 m_arg_idx
+ 1, ev
.m_expr
);
429 class use_after_free
: public malloc_diagnostic
432 use_after_free (const malloc_state_machine
&sm
, tree arg
)
433 : malloc_diagnostic (sm
, arg
)
436 const char *get_kind () const FINAL OVERRIDE
{ return "use_after_free"; }
438 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
440 /* CWE-416: Use After Free. */
441 diagnostic_metadata m
;
443 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_use_after_free
,
444 "use after %<free%> of %qE", m_arg
);
447 label_text
describe_state_change (const evdesc::state_change
&change
)
450 if (change
.m_new_state
== m_sm
.m_freed
)
452 m_free_event
= change
.m_event_id
;
453 return label_text::borrow ("freed here");
455 return malloc_diagnostic::describe_state_change (change
);
458 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
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
);
464 return ev
.formatted_print ("use after %<free%> of %qE", ev
.m_expr
);
468 diagnostic_event_id_t m_free_event
;
471 class malloc_leak
: public malloc_diagnostic
474 malloc_leak (const malloc_state_machine
&sm
, tree arg
)
475 : malloc_diagnostic (sm
, arg
) {}
477 const char *get_kind () const FINAL OVERRIDE
{ return "malloc_leak"; }
479 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
481 diagnostic_metadata m
;
483 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_malloc_leak
,
484 "leak of %qE", m_arg
);
487 label_text
describe_state_change (const evdesc::state_change
&change
)
490 if (change
.m_new_state
== m_sm
.m_unchecked
)
492 m_malloc_event
= change
.m_event_id
;
493 return label_text::borrow ("allocated here");
495 return malloc_diagnostic::describe_state_change (change
);
498 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
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
);
504 return ev
.formatted_print ("%qE leaks here", ev
.m_expr
);
508 diagnostic_event_id_t m_malloc_event
;
511 class free_of_non_heap
: public malloc_diagnostic
514 free_of_non_heap (const malloc_state_machine
&sm
, tree arg
)
515 : malloc_diagnostic (sm
, arg
), m_kind (KIND_UNKNOWN
)
519 const char *get_kind () const FINAL OVERRIDE
{ return "free_of_non_heap"; }
521 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
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
);
528 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
530 auto_diagnostic_group d
;
531 diagnostic_metadata m
;
532 m
.add_cwe (590); /* CWE-590: Free of Memory not on the Heap. */
538 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_free_of_non_heap
,
539 "%<free%> of %qE which points to memory"
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",
552 label_text
describe_state_change (const evdesc::state_change
&change
)
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
)
559 gimple
*def_stmt
= SSA_NAME_DEF_STMT (change
.m_expr
);
560 if (gcall
*call
= dyn_cast
<gcall
*> (def_stmt
))
562 if (is_special_named_call_p (call
, "alloca", 1)
563 || is_special_named_call_p (call
, "__builtin_alloca", 1))
565 m_kind
= KIND_ALLOCA
;
566 return label_text::borrow
567 ("memory is allocated on the stack here");
571 return label_text::borrow ("pointer is from here");
574 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
576 return ev
.formatted_print ("call to %qs here", "free");
588 /* malloc_state_machine's ctor. */
590 malloc_state_machine::malloc_state_machine (logger
*logger
)
591 : state_machine ("malloc", logger
)
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");
602 /* Implementation of state_machine::on_stmt vfunc for malloc_state_machine. */
605 malloc_state_machine::on_stmt (sm_context
*sm_ctxt
,
606 const supernode
*node
,
607 const gimple
*stmt
) const
609 if (const gcall
*call
= dyn_cast
<const gcall
*> (stmt
))
610 if (tree callee_fndecl
= sm_ctxt
->get_fndecl_for_call (call
))
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))
619 tree lhs
= gimple_call_lhs (call
);
622 lhs
= sm_ctxt
->get_readable_tree (lhs
);
623 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
, m_unchecked
);
627 /* TODO: report leak. */
632 if (is_named_call_p (callee_fndecl
, "alloca", call
, 1)
633 || is_named_call_p (callee_fndecl
, "__builtin_alloca", call
, 1))
635 tree lhs
= gimple_call_lhs (call
);
638 lhs
= sm_ctxt
->get_readable_tree (lhs
);
639 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
, m_non_heap
);
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))
648 tree arg
= gimple_call_arg (call
, 0);
650 arg
= sm_ctxt
->get_readable_tree (arg
);
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
);
657 /* Keep state "null" as-is, rather than transitioning to "free";
658 we don't want to complain about double-free of NULL. */
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
);
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
);
672 /* Handle "__attribute__((nonnull))". */
674 tree fntype
= TREE_TYPE (callee_fndecl
);
675 bitmap nonnull_args
= get_nonnull_args (fntype
);
678 for (unsigned i
= 0; i
< gimple_call_num_args (stmt
); i
++)
680 tree arg
= gimple_call_arg (stmt
, i
);
681 if (TREE_CODE (TREE_TYPE (arg
)) != POINTER_TYPE
)
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
))
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
,
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
);
700 BITMAP_FREE (nonnull_args
);
705 if (tree lhs
= is_zero_assignment (stmt
))
706 if (any_pointer_p (lhs
))
707 on_zero_assignment (sm_ctxt
, node
, stmt
,lhs
);
709 if (const gassign
*assign_stmt
= dyn_cast
<const gassign
*> (stmt
))
711 enum tree_code op
= gimple_assign_rhs_code (assign_stmt
);
714 tree lhs
= gimple_assign_lhs (assign_stmt
);
717 lhs
= sm_ctxt
->get_readable_tree (lhs
);
718 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
, m_non_heap
);
723 /* Handle dereferences. */
724 for (unsigned i
= 0; i
< gimple_num_ops (stmt
); i
++)
726 tree op
= gimple_op (stmt
, i
);
729 if (TREE_CODE (op
) == COMPONENT_REF
)
730 op
= TREE_OPERAND (op
, 0);
732 if (TREE_CODE (op
) == MEM_REF
)
734 tree arg
= TREE_OPERAND (op
, 0);
735 arg
= sm_ctxt
->get_readable_tree (arg
);
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
);
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
);
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
);
753 /* Implementation of state_machine::on_phi vfunc for malloc_state_machine. */
756 malloc_state_machine::on_phi (sm_context
*sm_ctxt
,
757 const supernode
*node
,
763 tree lhs
= gimple_phi_result (phi
);
764 on_zero_assignment (sm_ctxt
, node
, phi
, lhs
);
768 /* Implementation of state_machine::on_condition vfunc for malloc_state_machine.
769 Potentially transition state 'unchecked' to 'nonnull' or to 'null'. */
772 malloc_state_machine::on_condition (sm_context
*sm_ctxt
,
773 const supernode
*node
,
782 if (!any_pointer_p (lhs
))
784 if (!any_pointer_p (rhs
))
789 log ("got 'ARG != 0' match");
790 sm_ctxt
->on_transition (node
, stmt
,
791 lhs
, m_unchecked
, m_nonnull
);
793 else if (op
== EQ_EXPR
)
795 log ("got 'ARG == 0' match");
796 sm_ctxt
->on_transition (node
, stmt
,
797 lhs
, m_unchecked
, m_null
);
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). */
806 malloc_state_machine::can_purge_p (state_t s
) const
808 return s
!= m_unchecked
&& s
!= m_nonnull
;
811 /* Implementation of state_machine::on_leak vfunc for malloc_state_machine
812 (for complaining about leaks of pointers in state 'unchecked' and
816 malloc_state_machine::on_leak (tree var
) const
818 return new malloc_leak (*this, var
);
821 /* Shared logic for handling GIMPLE_ASSIGNs and GIMPLE_PHIs that
822 assign zero to LHS. */
825 malloc_state_machine::on_zero_assignment (sm_context
*sm_ctxt
,
826 const supernode
*node
,
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
);
836 } // anonymous namespace
838 /* Internal interface to this file. */
841 make_malloc_state_machine (logger
*logger
)
843 return new malloc_state_machine (logger
);
848 #endif /* #if ENABLE_ANALYZER */