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_condition (sm_context
*sm_ctxt
,
61 const supernode
*node
,
65 tree rhs
) const FINAL OVERRIDE
;
67 bool can_purge_p (state_t s
) const FINAL OVERRIDE
;
68 pending_diagnostic
*on_leak (tree var
) const FINAL OVERRIDE
;
73 /* State for a pointer returned from malloc that hasn't been checked for
75 It could be a pointer to heap-allocated memory, or could be NULL. */
78 /* State for a pointer that's known to be NULL. */
81 /* State for a pointer to heap-allocated memory, known to be non-NULL. */
84 /* State for a pointer to freed memory. */
87 /* State for a pointer that's known to not be on the heap (e.g. to a local
89 state_t m_non_heap
; // TODO: or should this be a different state machine?
90 // or do we need child values etc?
92 /* Stop state, for pointers we don't want to track any more. */
96 /* Class for diagnostics relating to malloc_state_machine. */
98 class malloc_diagnostic
: public pending_diagnostic
101 malloc_diagnostic (const malloc_state_machine
&sm
, tree arg
)
102 : m_sm (sm
), m_arg (arg
)
105 bool subclass_equal_p (const pending_diagnostic
&base_other
) const OVERRIDE
107 return same_tree_p (m_arg
, ((const malloc_diagnostic
&)base_other
).m_arg
);
110 label_text
describe_state_change (const evdesc::state_change
&change
)
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",
121 if (change
.m_new_state
== m_sm
.m_null
)
122 return change
.formatted_print ("assuming %qE is NULL",
124 return label_text ();
128 const malloc_state_machine
&m_sm
;
132 /* Concrete subclass for reporting double-free diagnostics. */
134 class double_free
: public malloc_diagnostic
137 double_free (const malloc_state_machine
&sm
, tree arg
)
138 : malloc_diagnostic (sm
, arg
)
141 const char *get_kind () const FINAL OVERRIDE
{ return "double_free"; }
143 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
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
);
152 label_text
describe_state_change (const evdesc::state_change
&change
)
155 if (change
.m_new_state
== m_sm
.m_freed
)
157 m_first_free_event
= change
.m_event_id
;
158 return change
.formatted_print ("first %qs here", "free");
160 return malloc_diagnostic::describe_state_change (change
);
163 label_text
describe_call_with_state (const evdesc::call_with_state
&info
)
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 ();
173 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
175 if (m_first_free_event
.known_p ())
176 return ev
.formatted_print ("second %qs here; first %qs was at %@",
178 &m_first_free_event
);
179 return ev
.formatted_print ("second %qs here", "free");
183 diagnostic_event_id_t m_first_free_event
;
186 /* Abstract subclass for describing possible bad uses of NULL.
187 Responsible for describing the call that could return NULL. */
189 class possible_null
: public malloc_diagnostic
192 possible_null (const malloc_state_machine
&sm
, tree arg
)
193 : malloc_diagnostic (sm
, arg
)
196 label_text
describe_state_change (const evdesc::state_change
&change
)
199 if (change
.m_old_state
== m_sm
.m_start
200 && change
.m_new_state
== m_sm
.m_unchecked
)
202 m_origin_of_unchecked_event
= change
.m_event_id
;
203 return label_text::borrow ("this call could return NULL");
205 return malloc_diagnostic::describe_state_change (change
);
208 label_text
describe_return_of_state (const evdesc::return_of_state
&info
)
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 ();
218 diagnostic_event_id_t m_origin_of_unchecked_event
;
221 /* Concrete subclass for describing dereference of a possible NULL
224 class possible_null_deref
: public possible_null
227 possible_null_deref (const malloc_state_machine
&sm
, tree arg
)
228 : possible_null (sm
, arg
)
231 const char *get_kind () const FINAL OVERRIDE
{ return "possible_null_deref"; }
233 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
235 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
236 diagnostic_metadata m
;
238 return warning_meta (rich_loc
, m
,
239 OPT_Wanalyzer_possible_null_dereference
,
240 "dereference of possibly-NULL %qE", m_arg
);
243 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
245 if (m_origin_of_unchecked_event
.known_p ())
246 return ev
.formatted_print ("%qE could be NULL: unchecked value from %@",
248 &m_origin_of_unchecked_event
);
250 return ev
.formatted_print ("%qE could be NULL", ev
.m_expr
);
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. */
259 inform_nonnull_attribute (tree fndecl
, int arg_idx
)
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
267 For reference, the C and C++ FEs have get_fndecl_argument_location. */
270 /* Concrete subclass for describing passing a possibly-NULL value to a
271 function marked with __attribute__((nonnull)). */
273 class possible_null_arg
: public possible_null
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
)
282 const char *get_kind () const FINAL OVERRIDE
{ return "possible_null_arg"; }
284 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
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
);
294 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
296 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
297 auto_diagnostic_group d
;
298 diagnostic_metadata m
;
301 = warning_meta (rich_loc
, m
, OPT_Wanalyzer_possible_null_argument
,
302 "use of possibly-NULL %qE where non-null expected",
305 inform_nonnull_attribute (m_fndecl
, m_arg_idx
);
309 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
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
);
317 return ev
.formatted_print ("argument %u (%qE) could be NULL"
318 " where non-null expected",
319 m_arg_idx
+ 1, ev
.m_expr
);
327 /* Concrete subclass for describing a dereference of a NULL value. */
329 class null_deref
: public malloc_diagnostic
332 null_deref (const malloc_state_machine
&sm
, tree arg
)
333 : malloc_diagnostic (sm
, arg
) {}
335 const char *get_kind () const FINAL OVERRIDE
{ return "null_deref"; }
337 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
339 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
340 diagnostic_metadata m
;
342 return warning_meta (rich_loc
, m
,
343 OPT_Wanalyzer_null_dereference
,
344 "dereference of NULL %qE", m_arg
);
347 label_text
describe_return_of_state (const evdesc::return_of_state
&info
)
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 ();
356 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
358 return ev
.formatted_print ("dereference of NULL %qE", ev
.m_expr
);
362 /* Concrete subclass for describing passing a NULL value to a
363 function marked with __attribute__((nonnull)). */
365 class null_arg
: public malloc_diagnostic
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
)
374 const char *get_kind () const FINAL OVERRIDE
{ return "null_arg"; }
376 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
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
);
385 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
387 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
388 auto_diagnostic_group d
;
389 diagnostic_metadata m
;
391 bool warned
= warning_meta (rich_loc
, m
, OPT_Wanalyzer_null_argument
,
392 "use of NULL %qE where non-null expected",
395 inform_nonnull_attribute (m_fndecl
, m_arg_idx
);
399 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
401 return ev
.formatted_print ("argument %u (%qE) NULL"
402 " where non-null expected",
403 m_arg_idx
+ 1, ev
.m_expr
);
411 class use_after_free
: public malloc_diagnostic
414 use_after_free (const malloc_state_machine
&sm
, tree arg
)
415 : malloc_diagnostic (sm
, arg
)
418 const char *get_kind () const FINAL OVERRIDE
{ return "use_after_free"; }
420 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
422 /* CWE-416: Use After Free. */
423 diagnostic_metadata m
;
425 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_use_after_free
,
426 "use after %<free%> of %qE", m_arg
);
429 label_text
describe_state_change (const evdesc::state_change
&change
)
432 if (change
.m_new_state
== m_sm
.m_freed
)
434 m_free_event
= change
.m_event_id
;
435 return label_text::borrow ("freed here");
437 return malloc_diagnostic::describe_state_change (change
);
440 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
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
);
446 return ev
.formatted_print ("use after %<free%> of %qE", ev
.m_expr
);
450 diagnostic_event_id_t m_free_event
;
453 class malloc_leak
: public malloc_diagnostic
456 malloc_leak (const malloc_state_machine
&sm
, tree arg
)
457 : malloc_diagnostic (sm
, arg
) {}
459 const char *get_kind () const FINAL OVERRIDE
{ return "malloc_leak"; }
461 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
463 diagnostic_metadata m
;
465 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_malloc_leak
,
466 "leak of %qE", m_arg
);
469 label_text
describe_state_change (const evdesc::state_change
&change
)
472 if (change
.m_new_state
== m_sm
.m_unchecked
)
474 m_malloc_event
= change
.m_event_id
;
475 return label_text::borrow ("allocated here");
477 return malloc_diagnostic::describe_state_change (change
);
480 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
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
);
486 return ev
.formatted_print ("%qE leaks here", ev
.m_expr
);
490 diagnostic_event_id_t m_malloc_event
;
493 class free_of_non_heap
: public malloc_diagnostic
496 free_of_non_heap (const malloc_state_machine
&sm
, tree arg
)
497 : malloc_diagnostic (sm
, arg
), m_kind (KIND_UNKNOWN
)
501 const char *get_kind () const FINAL OVERRIDE
{ return "free_of_non_heap"; }
503 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
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
);
510 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
512 auto_diagnostic_group d
;
513 diagnostic_metadata m
;
514 m
.add_cwe (590); /* CWE-590: Free of Memory not on the Heap. */
520 return warning_meta (rich_loc
, m
, OPT_Wanalyzer_free_of_non_heap
,
521 "%<free%> of %qE which points to memory"
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",
534 label_text
describe_state_change (const evdesc::state_change
&change
)
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
)
541 gimple
*def_stmt
= SSA_NAME_DEF_STMT (change
.m_expr
);
542 if (gcall
*call
= dyn_cast
<gcall
*> (def_stmt
))
544 if (is_special_named_call_p (call
, "alloca", 1)
545 || is_special_named_call_p (call
, "__builtin_alloca", 1))
547 m_kind
= KIND_ALLOCA
;
548 return label_text::borrow
549 ("memory is allocated on the stack here");
553 return label_text::borrow ("pointer is from here");
556 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
558 return ev
.formatted_print ("call to %qs here", "free");
570 /* malloc_state_machine's ctor. */
572 malloc_state_machine::malloc_state_machine (logger
*logger
)
573 : state_machine ("malloc", logger
)
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");
584 /* Implementation of state_machine::on_stmt vfunc for malloc_state_machine. */
587 malloc_state_machine::on_stmt (sm_context
*sm_ctxt
,
588 const supernode
*node
,
589 const gimple
*stmt
) const
591 if (const gcall
*call
= dyn_cast
<const gcall
*> (stmt
))
592 if (tree callee_fndecl
= sm_ctxt
->get_fndecl_for_call (call
))
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))
599 tree lhs
= gimple_call_lhs (call
);
602 lhs
= sm_ctxt
->get_readable_tree (lhs
);
603 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
, m_unchecked
);
607 /* TODO: report leak. */
612 if (is_named_call_p (callee_fndecl
, "alloca", call
, 1)
613 || is_named_call_p (callee_fndecl
, "__builtin_alloca", call
, 1))
615 tree lhs
= gimple_call_lhs (call
);
618 lhs
= sm_ctxt
->get_readable_tree (lhs
);
619 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
, m_non_heap
);
624 if (is_named_call_p (callee_fndecl
, "free", call
, 1)
625 || is_named_call_p (callee_fndecl
, "__builtin_free", call
, 1))
627 tree arg
= gimple_call_arg (call
, 0);
629 arg
= sm_ctxt
->get_readable_tree (arg
);
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
);
636 /* Keep state "null" as-is, rather than transitioning to "free";
637 we don't want want to complain about double-free of NULL. */
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
);
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
);
651 /* Handle "__attribute__((nonnull))". */
653 tree fntype
= TREE_TYPE (callee_fndecl
);
654 bitmap nonnull_args
= get_nonnull_args (fntype
);
657 for (unsigned i
= 0; i
< gimple_call_num_args (stmt
); i
++)
659 tree arg
= gimple_call_arg (stmt
, i
);
660 if (TREE_CODE (TREE_TYPE (arg
)) != POINTER_TYPE
)
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
))
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
,
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
);
679 BITMAP_FREE (nonnull_args
);
684 if (tree lhs
= is_zero_assignment (stmt
))
686 if (any_pointer_p (lhs
))
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
);
695 if (const gassign
*assign_stmt
= dyn_cast
<const gassign
*> (stmt
))
697 enum tree_code op
= gimple_assign_rhs_code (assign_stmt
);
700 tree lhs
= gimple_assign_lhs (assign_stmt
);
703 lhs
= sm_ctxt
->get_readable_tree (lhs
);
704 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
, m_non_heap
);
709 /* Handle dereferences. */
710 for (unsigned i
= 0; i
< gimple_num_ops (stmt
); i
++)
712 tree op
= gimple_op (stmt
, i
);
715 if (TREE_CODE (op
) == COMPONENT_REF
)
716 op
= TREE_OPERAND (op
, 0);
718 if (TREE_CODE (op
) == MEM_REF
)
720 tree arg
= TREE_OPERAND (op
, 0);
721 arg
= sm_ctxt
->get_readable_tree (arg
);
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
);
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
);
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
);
739 /* Implementation of state_machine::on_condition vfunc for malloc_state_machine.
740 Potentially transition state 'unchecked' to 'nonnull' or to 'null'. */
743 malloc_state_machine::on_condition (sm_context
*sm_ctxt
,
744 const supernode
*node
,
753 if (!any_pointer_p (lhs
))
755 if (!any_pointer_p (rhs
))
760 log ("got 'ARG != 0' match");
761 sm_ctxt
->on_transition (node
, stmt
,
762 lhs
, m_unchecked
, m_nonnull
);
764 else if (op
== EQ_EXPR
)
766 log ("got 'ARG == 0' match");
767 sm_ctxt
->on_transition (node
, stmt
,
768 lhs
, m_unchecked
, m_null
);
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). */
777 malloc_state_machine::can_purge_p (state_t s
) const
779 return s
!= m_unchecked
&& s
!= m_nonnull
;
782 /* Implementation of state_machine::on_leak vfunc for malloc_state_machine
783 (for complaining about leaks of pointers in state 'unchecked' and
787 malloc_state_machine::on_leak (tree var
) const
789 return new malloc_leak (*this, var
);
792 } // anonymous namespace
794 /* Internal interface to this file. */
797 make_malloc_state_machine (logger
*logger
)
799 return new malloc_state_machine (logger
);
804 #endif /* #if ENABLE_ANALYZER */