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_at (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_at (rich_loc
, m
, OPT_Wanalyzer_possible_null_dereference
,
239 "dereference of possibly-NULL %qE", m_arg
);
242 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
244 if (m_origin_of_unchecked_event
.known_p ())
245 return ev
.formatted_print ("%qE could be NULL: unchecked value from %@",
247 &m_origin_of_unchecked_event
);
249 return ev
.formatted_print ("%qE could be NULL", ev
.m_expr
);
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. */
258 inform_nonnull_attribute (tree fndecl
, int arg_idx
)
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
266 For reference, the C and C++ FEs have get_fndecl_argument_location. */
269 /* Concrete subclass for describing passing a possibly-NULL value to a
270 function marked with __attribute__((nonnull)). */
272 class possible_null_arg
: public possible_null
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
)
281 const char *get_kind () const FINAL OVERRIDE
{ return "possible_null_arg"; }
283 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
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
);
293 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
295 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
296 auto_diagnostic_group d
;
297 diagnostic_metadata m
;
300 = warning_at (rich_loc
, m
, OPT_Wanalyzer_possible_null_argument
,
301 "use of possibly-NULL %qE where non-null expected",
304 inform_nonnull_attribute (m_fndecl
, m_arg_idx
);
308 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
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
);
316 return ev
.formatted_print ("argument %u (%qE) could be NULL"
317 " where non-null expected",
318 m_arg_idx
+ 1, ev
.m_expr
);
326 /* Concrete subclass for describing a dereference of a NULL value. */
328 class null_deref
: public malloc_diagnostic
331 null_deref (const malloc_state_machine
&sm
, tree arg
)
332 : malloc_diagnostic (sm
, arg
) {}
334 const char *get_kind () const FINAL OVERRIDE
{ return "null_deref"; }
336 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
338 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
339 diagnostic_metadata m
;
341 return warning_at (rich_loc
, m
, OPT_Wanalyzer_null_dereference
,
342 "dereference of NULL %qE", m_arg
);
345 label_text
describe_return_of_state (const evdesc::return_of_state
&info
)
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 ();
354 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
356 return ev
.formatted_print ("dereference of NULL %qE", ev
.m_expr
);
360 /* Concrete subclass for describing passing a NULL value to a
361 function marked with __attribute__((nonnull)). */
363 class null_arg
: public malloc_diagnostic
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
)
372 const char *get_kind () const FINAL OVERRIDE
{ return "null_arg"; }
374 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
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
);
383 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
385 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */
386 auto_diagnostic_group d
;
387 diagnostic_metadata m
;
389 bool warned
= warning_at (rich_loc
, m
, OPT_Wanalyzer_null_argument
,
390 "use of NULL %qE where non-null expected", m_arg
);
392 inform_nonnull_attribute (m_fndecl
, m_arg_idx
);
396 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
398 return ev
.formatted_print ("argument %u (%qE) NULL"
399 " where non-null expected",
400 m_arg_idx
+ 1, ev
.m_expr
);
408 class use_after_free
: public malloc_diagnostic
411 use_after_free (const malloc_state_machine
&sm
, tree arg
)
412 : malloc_diagnostic (sm
, arg
)
415 const char *get_kind () const FINAL OVERRIDE
{ return "use_after_free"; }
417 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
419 /* CWE-416: Use After Free. */
420 diagnostic_metadata m
;
422 return warning_at (rich_loc
, m
, OPT_Wanalyzer_use_after_free
,
423 "use after %<free%> of %qE", m_arg
);
426 label_text
describe_state_change (const evdesc::state_change
&change
)
429 if (change
.m_new_state
== m_sm
.m_freed
)
431 m_free_event
= change
.m_event_id
;
432 return label_text::borrow ("freed here");
434 return malloc_diagnostic::describe_state_change (change
);
437 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
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
);
443 return ev
.formatted_print ("use after %<free%> of %qE", ev
.m_expr
);
447 diagnostic_event_id_t m_free_event
;
450 class malloc_leak
: public malloc_diagnostic
453 malloc_leak (const malloc_state_machine
&sm
, tree arg
)
454 : malloc_diagnostic (sm
, arg
) {}
456 const char *get_kind () const FINAL OVERRIDE
{ return "malloc_leak"; }
458 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
460 diagnostic_metadata m
;
462 return warning_at (rich_loc
, m
, OPT_Wanalyzer_malloc_leak
,
463 "leak of %qE", m_arg
);
466 label_text
describe_state_change (const evdesc::state_change
&change
)
469 if (change
.m_new_state
== m_sm
.m_unchecked
)
471 m_malloc_event
= change
.m_event_id
;
472 return label_text::borrow ("allocated here");
474 return malloc_diagnostic::describe_state_change (change
);
477 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
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
);
483 return ev
.formatted_print ("%qE leaks here", ev
.m_expr
);
487 diagnostic_event_id_t m_malloc_event
;
490 class free_of_non_heap
: public malloc_diagnostic
493 free_of_non_heap (const malloc_state_machine
&sm
, tree arg
)
494 : malloc_diagnostic (sm
, arg
), m_kind (KIND_UNKNOWN
)
498 const char *get_kind () const FINAL OVERRIDE
{ return "free_of_non_heap"; }
500 bool subclass_equal_p (const pending_diagnostic
&base_other
) const
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
);
507 bool emit (rich_location
*rich_loc
) FINAL OVERRIDE
509 auto_diagnostic_group d
;
510 diagnostic_metadata m
;
511 m
.add_cwe (590); /* CWE-590: Free of Memory not on the Heap. */
517 return warning_at (rich_loc
, m
, OPT_Wanalyzer_free_of_non_heap
,
518 "%<free%> of %qE which points to memory"
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",
531 label_text
describe_state_change (const evdesc::state_change
&change
)
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
)
538 gimple
*def_stmt
= SSA_NAME_DEF_STMT (change
.m_expr
);
539 if (gcall
*call
= dyn_cast
<gcall
*> (def_stmt
))
541 if (is_special_named_call_p (call
, "alloca", 1)
542 || is_special_named_call_p (call
, "__builtin_alloca", 1))
544 m_kind
= KIND_ALLOCA
;
545 return label_text::borrow
546 ("memory is allocated on the stack here");
550 return label_text::borrow ("pointer is from here");
553 label_text
describe_final_event (const evdesc::final_event
&ev
) FINAL OVERRIDE
555 return ev
.formatted_print ("call to %qs here", "free");
567 /* malloc_state_machine's ctor. */
569 malloc_state_machine::malloc_state_machine (logger
*logger
)
570 : state_machine ("malloc", logger
)
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");
581 /* Implementation of state_machine::on_stmt vfunc for malloc_state_machine. */
584 malloc_state_machine::on_stmt (sm_context
*sm_ctxt
,
585 const supernode
*node
,
586 const gimple
*stmt
) const
588 if (const gcall
*call
= dyn_cast
<const gcall
*> (stmt
))
589 if (tree callee_fndecl
= sm_ctxt
->get_fndecl_for_call (call
))
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))
596 tree lhs
= gimple_call_lhs (call
);
599 lhs
= sm_ctxt
->get_readable_tree (lhs
);
600 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
, m_unchecked
);
604 /* TODO: report leak. */
609 if (is_named_call_p (callee_fndecl
, "alloca", call
, 1)
610 || is_named_call_p (callee_fndecl
, "__builtin_alloca", call
, 1))
612 tree lhs
= gimple_call_lhs (call
);
615 lhs
= sm_ctxt
->get_readable_tree (lhs
);
616 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
, m_non_heap
);
621 if (is_named_call_p (callee_fndecl
, "free", call
, 1)
622 || is_named_call_p (callee_fndecl
, "__builtin_free", call
, 1))
624 tree arg
= gimple_call_arg (call
, 0);
626 arg
= sm_ctxt
->get_readable_tree (arg
);
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
);
633 /* Keep state "null" as-is, rather than transitioning to "free";
634 we don't want want to complain about double-free of NULL. */
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
);
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
);
648 /* Handle "__attribute__((nonnull))". */
650 tree fntype
= TREE_TYPE (callee_fndecl
);
651 bitmap nonnull_args
= get_nonnull_args (fntype
);
654 for (unsigned i
= 0; i
< gimple_call_num_args (stmt
); i
++)
656 tree arg
= gimple_call_arg (stmt
, i
);
657 if (TREE_CODE (TREE_TYPE (arg
)) != POINTER_TYPE
)
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
))
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
,
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
);
676 BITMAP_FREE (nonnull_args
);
681 if (tree lhs
= is_zero_assignment (stmt
))
683 if (any_pointer_p (lhs
))
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
);
692 if (const gassign
*assign_stmt
= dyn_cast
<const gassign
*> (stmt
))
694 enum tree_code op
= gimple_assign_rhs_code (assign_stmt
);
697 tree lhs
= gimple_assign_lhs (assign_stmt
);
700 lhs
= sm_ctxt
->get_readable_tree (lhs
);
701 sm_ctxt
->on_transition (node
, stmt
, lhs
, m_start
, m_non_heap
);
706 /* Handle dereferences. */
707 for (unsigned i
= 0; i
< gimple_num_ops (stmt
); i
++)
709 tree op
= gimple_op (stmt
, i
);
712 if (TREE_CODE (op
) == COMPONENT_REF
)
713 op
= TREE_OPERAND (op
, 0);
715 if (TREE_CODE (op
) == MEM_REF
)
717 tree arg
= TREE_OPERAND (op
, 0);
718 arg
= sm_ctxt
->get_readable_tree (arg
);
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
);
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
);
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
);
736 /* Implementation of state_machine::on_condition vfunc for malloc_state_machine.
737 Potentially transition state 'unchecked' to 'nonnull' or to 'null'. */
740 malloc_state_machine::on_condition (sm_context
*sm_ctxt
,
741 const supernode
*node
,
750 if (!any_pointer_p (lhs
))
752 if (!any_pointer_p (rhs
))
757 log ("got 'ARG != 0' match");
758 sm_ctxt
->on_transition (node
, stmt
,
759 lhs
, m_unchecked
, m_nonnull
);
761 else if (op
== EQ_EXPR
)
763 log ("got 'ARG == 0' match");
764 sm_ctxt
->on_transition (node
, stmt
,
765 lhs
, m_unchecked
, m_null
);
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). */
774 malloc_state_machine::can_purge_p (state_t s
) const
776 return s
!= m_unchecked
&& s
!= m_nonnull
;
779 /* Implementation of state_machine::on_leak vfunc for malloc_state_machine
780 (for complaining about leaks of pointers in state 'unchecked' and
784 malloc_state_machine::on_leak (tree var
) const
786 return new malloc_leak (*this, var
);
789 } // anonymous namespace
791 /* Internal interface to this file. */
794 make_malloc_state_machine (logger
*logger
)
796 return new malloc_state_machine (logger
);
801 #endif /* #if ENABLE_ANALYZER */