aboutsummaryrefslogtreecommitdiff
path: root/gcc/analyzer/sm-malloc.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/analyzer/sm-malloc.cc')
-rw-r--r--gcc/analyzer/sm-malloc.cc316
1 files changed, 201 insertions, 115 deletions
diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc
index 6972a55..a6b1421 100644
--- a/gcc/analyzer/sm-malloc.cc
+++ b/gcc/analyzer/sm-malloc.cc
@@ -18,21 +18,13 @@ You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
-#include "config.h"
-#define INCLUDE_VECTOR
-#include "system.h"
-#include "coretypes.h"
-#include "make-unique.h"
-#include "tree.h"
-#include "function.h"
-#include "basic-block.h"
-#include "gimple.h"
-#include "options.h"
-#include "bitmap.h"
-#include "diagnostic-core.h"
-#include "diagnostic-path.h"
-#include "analyzer/analyzer.h"
-#include "diagnostic-event-id.h"
+#include "analyzer/common.h"
+
+#include "diagnostics/event-id.h"
+#include "stringpool.h"
+#include "attribs.h"
+#include "xml-printer.h"
+
#include "analyzer/analyzer-logging.h"
#include "analyzer/sm.h"
#include "analyzer/pending-diagnostic.h"
@@ -41,13 +33,12 @@ along with GCC; see the file COPYING3. If not see
#include "analyzer/store.h"
#include "analyzer/region-model.h"
#include "analyzer/call-details.h"
-#include "stringpool.h"
-#include "attribs.h"
#include "analyzer/function-set.h"
#include "analyzer/program-state.h"
#include "analyzer/checker-event.h"
#include "analyzer/exploded-graph.h"
#include "analyzer/inlining-iterator.h"
+#include "analyzer/ana-state-to-diagnostic-state.h"
#if ENABLE_ANALYZER
@@ -150,7 +141,7 @@ struct assumed_non_null_state : public allocation_state
assumed_non_null_state (const char *name, unsigned id,
const frame_region *frame)
: allocation_state (name, id, RS_ASSUMED_NON_NULL,
- NULL, NULL),
+ nullptr, nullptr),
m_frame (frame)
{
gcc_assert (m_frame);
@@ -301,7 +292,7 @@ struct deallocator_set_map_traits
static inline hashval_t hash (const key_type &k)
{
- gcc_assert (k != NULL);
+ gcc_assert (k != nullptr);
gcc_assert (k != reinterpret_cast<key_type> (1));
hashval_t result = 0;
@@ -335,7 +326,7 @@ struct deallocator_set_map_traits
template <typename T>
static inline void mark_empty (T &entry)
{
- entry.m_key = NULL;
+ entry.m_key = nullptr;
}
template <typename T>
static inline bool is_deleted (const T &entry)
@@ -345,7 +336,7 @@ struct deallocator_set_map_traits
template <typename T>
static inline bool is_empty (const T &entry)
{
- return entry.m_key == NULL;
+ return entry.m_key == nullptr;
}
static const bool empty_zero_p = false;
};
@@ -414,7 +405,11 @@ public:
const frame_region *) const final override;
bool can_purge_p (state_t s) const final override;
- std::unique_ptr<pending_diagnostic> on_leak (tree var) const final override;
+
+ std::unique_ptr<pending_diagnostic>
+ on_leak (tree var,
+ const program_state *old_state,
+ const program_state *new_state) const final override;
bool reset_when_passed_to_unknown_fn_p (state_t s,
bool is_mutable) const final override;
@@ -440,6 +435,11 @@ public:
const svalue *new_ptr_sval,
const extrinsic_state &ext_state) const;
+ void
+ add_state_to_state_graph (analyzer_state_graph &out_state_graph,
+ const svalue &sval,
+ state_machine::state_t state) const final override;
+
standard_deallocator_set m_free;
standard_deallocator_set m_scalar_delete;
standard_deallocator_set m_vector_delete;
@@ -482,22 +482,22 @@ private:
tree ptr) const;
void on_allocator_call (sm_context &sm_ctxt,
- const gcall *call,
+ const gcall &call,
const deallocator_set *deallocators,
bool returns_nonnull = false) const;
void handle_free_of_non_heap (sm_context &sm_ctxt,
const supernode *node,
- const gcall *call,
+ const gcall &call,
tree arg,
const deallocator *d) const;
void on_deallocator_call (sm_context &sm_ctxt,
const supernode *node,
- const gcall *call,
+ const gcall &call,
const deallocator *d,
unsigned argno) const;
void on_realloc_call (sm_context &sm_ctxt,
const supernode *node,
- const gcall *call) const;
+ const gcall &call) const;
void on_zero_assignment (sm_context &sm_ctxt,
const gimple *stmt,
tree lhs) const;
@@ -535,7 +535,7 @@ deallocator::deallocator (malloc_state_machine *sm,
enum wording wording)
: m_name (name),
m_wording (wording),
- m_freed (sm->add_state ("freed", RS_FREED, NULL, this))
+ m_freed (sm->add_state ("freed", RS_FREED, nullptr, this))
{
}
@@ -579,8 +579,8 @@ standard_deallocator::standard_deallocator (malloc_state_machine *sm,
deallocator_set::deallocator_set (malloc_state_machine *sm,
enum wording wording)
: m_wording (wording),
- m_unchecked (sm->add_state ("unchecked", RS_UNCHECKED, this, NULL)),
- m_nonnull (sm->add_state ("nonnull", RS_NONNULL, this, NULL))
+ m_unchecked (sm->add_state ("unchecked", RS_UNCHECKED, this, nullptr)),
+ m_nonnull (sm->add_state ("nonnull", RS_NONNULL, this, nullptr))
{
}
@@ -625,7 +625,7 @@ custom_deallocator_set::maybe_get_single () const
{
if (m_deallocator_vec.length () == 1)
return m_deallocator_vec[0];
- return NULL;
+ return nullptr;
}
void
@@ -673,14 +673,15 @@ standard_deallocator_set::dump_to_pp (pretty_printer *pp) const
pp_character (pp, '}');
}
-/* Return STATE cast to the custom state subclass, or NULL for the start state.
+/* Return STATE cast to the custom state subclass, or nullptr for the
+ start state.
Everything should be an allocation_state apart from the start state. */
static const allocation_state *
dyn_cast_allocation_state (state_machine::state_t state)
{
if (state->get_id () == 0)
- return NULL;
+ return nullptr;
return static_cast <const allocation_state *> (state);
}
@@ -796,8 +797,13 @@ public:
else
{
if (change.m_expr)
- pp_printf (&pp, "%qE is NULL",
- change.m_expr);
+ {
+ if (zerop (change.m_expr))
+ pp_printf (&pp, "using NULL here");
+ else
+ pp_printf (&pp, "%qE is NULL",
+ change.m_expr);
+ }
else
pp_printf (&pp, "%qs is NULL",
"<unknown>");
@@ -808,18 +814,18 @@ public:
return false;
}
- diagnostic_event::meaning
+ diagnostics::paths::event::meaning
get_meaning_for_state_change (const evdesc::state_change &change)
const final override
{
if (change.m_old_state == m_sm.get_start_state ()
&& unchecked_p (change.m_new_state))
- return diagnostic_event::meaning (diagnostic_event::VERB_acquire,
- diagnostic_event::NOUN_memory);
+ return diagnostics::paths::event::meaning (diagnostics::paths::event::verb::acquire,
+ diagnostics::paths::event::noun::memory);
if (freed_p (change.m_new_state))
- return diagnostic_event::meaning (diagnostic_event::VERB_release,
- diagnostic_event::NOUN_memory);
- return diagnostic_event::meaning ();
+ return diagnostics::paths::event::meaning (diagnostics::paths::event::verb::release,
+ diagnostics::paths::event::noun::memory);
+ return diagnostics::paths::event::meaning ();
}
protected:
@@ -912,7 +918,7 @@ public:
}
private:
- diagnostic_event_id_t m_alloc_event;
+ diagnostics::paths::event_id_t m_alloc_event;
const deallocator_set *m_expected_deallocators;
const deallocator *m_actual_dealloc;
};
@@ -982,7 +988,7 @@ public:
}
private:
- diagnostic_event_id_t m_first_free_event;
+ diagnostics::paths::event_id_t m_first_free_event;
const char *m_funcname;
};
@@ -1025,7 +1031,7 @@ public:
}
protected:
- diagnostic_event_id_t m_origin_of_unchecked_event;
+ diagnostics::paths::event_id_t m_origin_of_unchecked_event;
};
/* Concrete subclass for describing dereference of a possible NULL
@@ -1413,15 +1419,21 @@ public:
}
private:
- diagnostic_event_id_t m_free_event;
+ diagnostics::paths::event_id_t m_free_event;
const deallocator *m_deallocator;
};
class malloc_leak : public malloc_diagnostic
{
public:
- malloc_leak (const malloc_state_machine &sm, tree arg)
- : malloc_diagnostic (sm, arg) {}
+ malloc_leak (const malloc_state_machine &sm, tree arg,
+ const program_state *final_state)
+ : malloc_diagnostic (sm, arg),
+ m_final_state ()
+ {
+ if (final_state)
+ m_final_state = std::make_unique<program_state> (*final_state);
+ }
const char *get_kind () const final override { return "malloc_leak"; }
@@ -1481,8 +1493,15 @@ public:
return true;
}
+ const program_state *
+ get_final_state () const final override
+ {
+ return m_final_state.get ();
+ }
+
private:
- diagnostic_event_id_t m_alloc_event;
+ diagnostics::paths::event_id_t m_alloc_event;
+ std::unique_ptr<program_state> m_final_state;
};
class free_of_non_heap : public malloc_diagnostic
@@ -1577,9 +1596,9 @@ class deref_before_check : public malloc_diagnostic
public:
deref_before_check (const malloc_state_machine &sm, tree arg)
: malloc_diagnostic (sm, arg),
- m_deref_enode (NULL),
- m_deref_expr (NULL),
- m_check_enode (NULL)
+ m_deref_enode (nullptr),
+ m_deref_expr (nullptr),
+ m_check_enode (nullptr)
{
gcc_assert (arg);
}
@@ -1749,7 +1768,7 @@ private:
return result;
}
- diagnostic_event_id_t m_first_deref_event;
+ diagnostics::paths::event_id_t m_first_deref_event;
const exploded_node *m_deref_enode;
tree m_deref_expr;
const exploded_node *m_check_enode;
@@ -1804,9 +1823,9 @@ malloc_state_machine::malloc_state_machine (logger *logger)
m_realloc (this, "realloc", WORDING_REALLOCATED)
{
gcc_assert (m_start->get_id () == 0);
- m_null = add_state ("null", RS_FREED, NULL, NULL);
- m_non_heap = add_state ("non-heap", RS_NON_HEAP, NULL, NULL);
- m_stop = add_state ("stop", RS_STOP, NULL, NULL);
+ m_null = add_state ("null", RS_FREED, nullptr, nullptr);
+ m_non_heap = add_state ("non-heap", RS_NON_HEAP, nullptr, nullptr);
+ m_stop = add_state ("stop", RS_STOP, nullptr, nullptr);
}
malloc_state_machine::~malloc_state_machine ()
@@ -1834,7 +1853,7 @@ malloc_state_machine::add_state (const char *name, enum resource_state rs,
return a custom_deallocator_set for them, consolidating them
to ensure uniqueness of the sets.
- Return NULL if it has no such attributes. */
+ Return nullptr if it has no such attributes. */
const custom_deallocator_set *
malloc_state_machine::
@@ -1843,7 +1862,7 @@ get_or_create_custom_deallocator_set (tree allocator_fndecl)
/* Early rejection of decls without attributes. */
tree attrs = DECL_ATTRIBUTES (allocator_fndecl);
if (!attrs)
- return NULL;
+ return nullptr;
/* Otherwise, call maybe_create_custom_deallocator_set,
memoizing the result. */
@@ -1861,7 +1880,7 @@ get_or_create_custom_deallocator_set (tree allocator_fndecl)
custom_deallocator_set for them, consolidating them
to ensure uniqueness of the sets.
- Return NULL if it has no such attributes.
+ Return nullptr if it has no such attributes.
Subroutine of get_or_create_custom_deallocator_set which
memoizes the result. */
@@ -1892,7 +1911,7 @@ maybe_create_custom_deallocator_set (tree allocator_fndecl)
/* If there weren't any deallocators, bail. */
if (deallocator_vec.length () == 0)
- return NULL;
+ return nullptr;
/* Consolidate, so that we reuse existing deallocator_set
instances. */
@@ -1955,7 +1974,7 @@ get_or_create_assumed_non_null_state_for_frame (const frame_region *frame)
builtin. */
static bool
-known_allocator_p (const_tree fndecl, const gcall *call)
+known_allocator_p (const_tree fndecl, const gcall &call)
{
/* Either it is a function we know by name and number of arguments... */
if (is_named_call_p (fndecl, "malloc", call, 1)
@@ -1997,7 +2016,7 @@ malloc_state_machine::maybe_assume_non_null (sm_context &sm_ctxt,
tree null_ptr_cst = build_int_cst (TREE_TYPE (ptr), 0);
tristate known_non_null
- = old_model->eval_condition (ptr, NE_EXPR, null_ptr_cst, NULL);
+ = old_model->eval_condition (ptr, NE_EXPR, null_ptr_cst, nullptr);
if (known_non_null.is_unknown ())
{
/* Cast away const-ness for cache-like operations. */
@@ -2029,9 +2048,10 @@ malloc_state_machine::handle_nonnull (sm_context &sm_ctxt,
if (unchecked_p (state))
{
tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
- sm_ctxt.warn (node, stmt, arg,
- make_unique<possible_null_arg> (*this, diag_arg, fndecl,
- i));
+ sm_ctxt.warn
+ (node, stmt, arg,
+ std::make_unique<possible_null_arg> (*this, diag_arg, fndecl,
+ i));
const allocation_state *astate
= as_a_allocation_state (state);
sm_ctxt.set_next_state (stmt, arg, astate->get_nonnull ());
@@ -2040,7 +2060,7 @@ malloc_state_machine::handle_nonnull (sm_context &sm_ctxt,
{
tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
sm_ctxt.warn (node, stmt, arg,
- make_unique<null_arg> (*this, diag_arg, fndecl, i));
+ std::make_unique<null_arg> (*this, diag_arg, fndecl, i));
sm_ctxt.set_next_state (stmt, arg, m_stop);
}
else if (state == m_start)
@@ -2054,9 +2074,11 @@ malloc_state_machine::on_stmt (sm_context &sm_ctxt,
const supernode *node,
const gimple *stmt) const
{
- if (const gcall *call = dyn_cast <const gcall *> (stmt))
- if (tree callee_fndecl = sm_ctxt.get_fndecl_for_call (call))
+ if (const gcall *call_stmt = dyn_cast <const gcall *> (stmt))
+ if (tree callee_fndecl = sm_ctxt.get_fndecl_for_call (*call_stmt))
{
+ const gcall &call = *call_stmt;
+
if (known_allocator_p (callee_fndecl, call))
{
on_allocator_call (sm_ctxt, call, &m_free);
@@ -2092,7 +2114,7 @@ malloc_state_machine::on_stmt (sm_context &sm_ctxt,
if (is_named_call_p (callee_fndecl, "alloca", call, 1)
|| is_named_call_p (callee_fndecl, "__builtin_alloca", call, 1))
{
- tree lhs = gimple_call_lhs (call);
+ tree lhs = gimple_call_lhs (&call);
if (lhs)
sm_ctxt.on_transition (node, stmt, lhs, m_start, m_non_heap);
return true;
@@ -2175,19 +2197,27 @@ malloc_state_machine::on_stmt (sm_context &sm_ctxt,
unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1;
unsigned int idx2
= TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1;
+ unsigned int idx3 = idx2;
+ if (tree chain2 = TREE_CHAIN (TREE_CHAIN (args)))
+ idx3 = TREE_INT_CST_LOW (TREE_VALUE (chain2)) - 1;
if (idx < gimple_call_num_args (stmt)
- && idx2 < gimple_call_num_args (stmt))
+ && idx2 < gimple_call_num_args (stmt)
+ && idx3 < gimple_call_num_args (stmt))
{
tree arg = gimple_call_arg (stmt, idx);
tree arg2 = gimple_call_arg (stmt, idx2);
+ tree arg3 = gimple_call_arg (stmt, idx3);
if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE
|| !INTEGRAL_TYPE_P (TREE_TYPE (arg2))
- || integer_zerop (arg2))
+ || !INTEGRAL_TYPE_P (TREE_TYPE (arg3))
+ || integer_zerop (arg2)
+ || integer_zerop (arg3))
continue;
- if (integer_nonzerop (arg2))
+ if (integer_nonzerop (arg2) && integer_nonzerop (arg3))
;
else
- /* FIXME: Use ranger here to query arg2 range? */
+ /* FIXME: Use ranger here to query arg2 and arg3
+ ranges? */
continue;
handle_nonnull (sm_ctxt, node, stmt, fndecl, arg, idx);
}
@@ -2260,8 +2290,8 @@ malloc_state_machine::on_stmt (sm_context &sm_ctxt,
{
tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
sm_ctxt.warn (node, stmt, arg,
- make_unique<possible_null_deref> (*this,
- diag_arg));
+ std::make_unique<possible_null_deref> (*this,
+ diag_arg));
const allocation_state *astate = as_a_allocation_state (state);
sm_ctxt.set_next_state (stmt, arg, astate->get_nonnull ());
}
@@ -2269,7 +2299,7 @@ malloc_state_machine::on_stmt (sm_context &sm_ctxt,
{
tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
sm_ctxt.warn (node, stmt, arg,
- make_unique<null_deref> (*this, diag_arg));
+ std::make_unique<null_deref> (*this, diag_arg));
sm_ctxt.set_next_state (stmt, arg, m_stop);
}
else if (freed_p (state))
@@ -2277,7 +2307,7 @@ malloc_state_machine::on_stmt (sm_context &sm_ctxt,
tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
const allocation_state *astate = as_a_allocation_state (state);
sm_ctxt.warn (node, stmt, arg,
- make_unique<use_after_free>
+ std::make_unique<use_after_free>
(*this, diag_arg, astate->m_deallocator));
sm_ctxt.set_next_state (stmt, arg, m_stop);
}
@@ -2339,7 +2369,7 @@ maybe_complain_about_deref_before_check (sm_context &sm_ctxt,
if (diag_ptr)
sm_ctxt.warn
(node, stmt, ptr,
- make_unique<deref_before_check> (*this, diag_ptr));
+ std::make_unique<deref_before_check> (*this, diag_ptr));
sm_ctxt.set_next_state (stmt, ptr, m_stop);
}
@@ -2349,15 +2379,15 @@ maybe_complain_about_deref_before_check (sm_context &sm_ctxt,
void
malloc_state_machine::on_allocator_call (sm_context &sm_ctxt,
- const gcall *call,
+ const gcall &call,
const deallocator_set *deallocators,
bool returns_nonnull) const
{
- tree lhs = gimple_call_lhs (call);
+ tree lhs = gimple_call_lhs (&call);
if (lhs)
{
- if (sm_ctxt.get_state (call, lhs) == m_start)
- sm_ctxt.set_next_state (call, lhs,
+ if (sm_ctxt.get_state (&call, lhs) == m_start)
+ sm_ctxt.set_next_state (&call, lhs,
(returns_nonnull
? deallocators->m_nonnull
: deallocators->m_unchecked));
@@ -2374,40 +2404,40 @@ malloc_state_machine::on_allocator_call (sm_context &sm_ctxt,
void
malloc_state_machine::handle_free_of_non_heap (sm_context &sm_ctxt,
const supernode *node,
- const gcall *call,
+ const gcall &call,
tree arg,
const deallocator *d) const
{
tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
- const region *freed_reg = NULL;
+ const region *freed_reg = nullptr;
if (const program_state *old_state = sm_ctxt.get_old_program_state ())
{
const region_model *old_model = old_state->m_region_model;
- const svalue *ptr_sval = old_model->get_rvalue (arg, NULL);
- freed_reg = old_model->deref_rvalue (ptr_sval, arg, NULL);
+ const svalue *ptr_sval = old_model->get_rvalue (arg, nullptr);
+ freed_reg = old_model->deref_rvalue (ptr_sval, arg, nullptr);
}
- sm_ctxt.warn (node, call, arg,
- make_unique<free_of_non_heap>
+ sm_ctxt.warn (node, &call, arg,
+ std::make_unique<free_of_non_heap>
(*this, diag_arg, freed_reg, d->m_name));
- sm_ctxt.set_next_state (call, arg, m_stop);
+ sm_ctxt.set_next_state (&call, arg, m_stop);
}
void
malloc_state_machine::on_deallocator_call (sm_context &sm_ctxt,
const supernode *node,
- const gcall *call,
+ const gcall &call,
const deallocator *d,
unsigned argno) const
{
- if (argno >= gimple_call_num_args (call))
+ if (argno >= gimple_call_num_args (&call))
return;
- tree arg = gimple_call_arg (call, argno);
+ tree arg = gimple_call_arg (&call, argno);
- state_t state = sm_ctxt.get_state (call, arg);
+ state_t state = sm_ctxt.get_state (&call, arg);
/* start/assumed_non_null/unchecked/nonnull -> freed. */
if (state == m_start || assumed_non_null_p (state))
- sm_ctxt.set_next_state (call, arg, d->m_freed);
+ sm_ctxt.set_next_state (&call, arg, d->m_freed);
else if (unchecked_p (state) || nonnull_p (state))
{
const allocation_state *astate = as_a_allocation_state (state);
@@ -2416,13 +2446,13 @@ malloc_state_machine::on_deallocator_call (sm_context &sm_ctxt,
{
/* Wrong allocator. */
tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
- sm_ctxt.warn (node, call, arg,
- make_unique<mismatching_deallocation>
+ sm_ctxt.warn (node, &call, arg,
+ std::make_unique<mismatching_deallocation>
(*this, diag_arg,
astate->m_deallocators,
d));
}
- sm_ctxt.set_next_state (call, arg, d->m_freed);
+ sm_ctxt.set_next_state (&call, arg, d->m_freed);
}
/* Keep state "null" as-is, rather than transitioning to "freed";
@@ -2431,9 +2461,9 @@ malloc_state_machine::on_deallocator_call (sm_context &sm_ctxt,
{
/* freed -> stop, with warning. */
tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
- sm_ctxt.warn (node, call, arg,
- make_unique<double_free> (*this, diag_arg, d->m_name));
- sm_ctxt.set_next_state (call, arg, m_stop);
+ sm_ctxt.warn (node, &call, arg,
+ std::make_unique<double_free> (*this, diag_arg, d->m_name));
+ sm_ctxt.set_next_state (&call, arg, m_stop);
}
else if (state == m_non_heap)
{
@@ -2453,14 +2483,14 @@ malloc_state_machine::on_deallocator_call (sm_context &sm_ctxt,
void
malloc_state_machine::on_realloc_call (sm_context &sm_ctxt,
const supernode *node,
- const gcall *call) const
+ const gcall &call) const
{
const unsigned argno = 0;
const deallocator *d = &m_realloc;
- tree arg = gimple_call_arg (call, argno);
+ tree arg = gimple_call_arg (&call, argno);
- state_t state = sm_ctxt.get_state (call, arg);
+ state_t state = sm_ctxt.get_state (&call, arg);
if (unchecked_p (state) || nonnull_p (state))
{
@@ -2470,11 +2500,11 @@ malloc_state_machine::on_realloc_call (sm_context &sm_ctxt,
{
/* Wrong allocator. */
tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
- sm_ctxt.warn (node, call, arg,
- make_unique<mismatching_deallocation>
+ sm_ctxt.warn (node, &call, arg,
+ std::make_unique<mismatching_deallocation>
(*this, diag_arg,
astate->m_deallocators, d));
- sm_ctxt.set_next_state (call, arg, m_stop);
+ sm_ctxt.set_next_state (&call, arg, m_stop);
if (path_context *path_ctxt = sm_ctxt.get_path_context ())
path_ctxt->terminate_path ();
}
@@ -2483,9 +2513,9 @@ malloc_state_machine::on_realloc_call (sm_context &sm_ctxt,
{
/* freed -> stop, with warning. */
tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
- sm_ctxt.warn (node, call, arg,
- make_unique<double_free> (*this, diag_arg, "free"));
- sm_ctxt.set_next_state (call, arg, m_stop);
+ sm_ctxt.warn (node, &call, arg,
+ std::make_unique<double_free> (*this, diag_arg, "free"));
+ sm_ctxt.set_next_state (&call, arg, m_stop);
if (path_context *path_ctxt = sm_ctxt.get_path_context ())
path_ctxt->terminate_path ();
}
@@ -2592,9 +2622,11 @@ malloc_state_machine::can_purge_p (state_t s) const
'nonnull'). */
std::unique_ptr<pending_diagnostic>
-malloc_state_machine::on_leak (tree var) const
+malloc_state_machine::on_leak (tree var,
+ const program_state *,
+ const program_state *new_state) const
{
- return make_unique<malloc_leak> (*this, var);
+ return std::make_unique<malloc_leak> (*this, var, new_state);
}
/* Implementation of state_machine::reset_when_passed_to_unknown_fn_p vfunc
@@ -2627,7 +2659,7 @@ malloc_state_machine::maybe_get_merged_states_nonequal (state_t state_a,
return m_start;
if (state_a == m_start && assumed_non_null_p (state_b))
return m_start;
- return NULL;
+ return nullptr;
}
/* Return true if calls to FNDECL are known to not affect this sm-state. */
@@ -2685,11 +2717,11 @@ on_realloc_with_move (region_model *model,
{
smap->set_state (model, old_ptr_sval,
m_free.m_deallocator.m_freed,
- NULL, ext_state);
+ nullptr, ext_state);
smap->set_state (model, new_ptr_sval,
m_free.m_nonnull,
- NULL, ext_state);
+ nullptr, ext_state);
}
/* Hook for get_or_create_region_for_heap_alloc for the case when we want
@@ -2700,17 +2732,71 @@ malloc_state_machine::transition_ptr_sval_non_null (region_model *model,
const svalue *new_ptr_sval,
const extrinsic_state &ext_state) const
{
- smap->set_state (model, new_ptr_sval, m_free.m_nonnull, NULL, ext_state);
+ smap->set_state (model, new_ptr_sval, m_free.m_nonnull, nullptr, ext_state);
+}
+
+static enum diagnostics::state_graphs::node_dynalloc_state
+get_dynalloc_state_for_state (enum resource_state rs)
+{
+ switch (rs)
+ {
+ default:
+ gcc_unreachable ();
+ case RS_START:
+ case RS_NULL:
+ case RS_NON_HEAP:
+ case RS_STOP:
+ return diagnostics::state_graphs::node_dynalloc_state::unknown;
+
+ case RS_ASSUMED_NON_NULL:
+ return diagnostics::state_graphs::node_dynalloc_state::nonnull;
+
+ case RS_UNCHECKED:
+ return diagnostics::state_graphs::node_dynalloc_state::unchecked;
+ case RS_NONNULL:
+ return diagnostics::state_graphs::node_dynalloc_state::nonnull;
+ case RS_FREED:
+ return diagnostics::state_graphs::node_dynalloc_state::freed;
+ }
+}
+
+void
+malloc_state_machine::
+add_state_to_state_graph (analyzer_state_graph &out_state_graph,
+ const svalue &sval,
+ state_machine::state_t state) const
+{
+ if (const region *reg = sval.maybe_get_region ())
+ {
+ auto reg_node = out_state_graph.get_or_create_state_node (*reg);
+ auto alloc_state = as_a_allocation_state (state);
+ gcc_assert (alloc_state);
+
+ reg_node.set_dynalloc_state
+ (get_dynalloc_state_for_state (alloc_state->m_rs));
+ if (alloc_state->m_deallocators)
+ {
+ pretty_printer pp;
+ alloc_state->m_deallocators->dump_to_pp (&pp);
+ reg_node.m_node.set_attr (STATE_NODE_PREFIX,
+ "expected-deallocators",
+ pp_formatted_text (&pp));
+ }
+ if (alloc_state->m_deallocator)
+ reg_node.m_node.set_attr (STATE_NODE_PREFIX,
+ "deallocator",
+ alloc_state->m_deallocator->m_name);
+ }
}
} // anonymous namespace
/* Internal interface to this file. */
-state_machine *
+std::unique_ptr<state_machine>
make_malloc_state_machine (logger *logger)
{
- return new malloc_state_machine (logger);
+ return std::make_unique<malloc_state_machine> (logger);
}
/* Specialcase hook for handling realloc, for use by