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.cc141
1 files changed, 100 insertions, 41 deletions
diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc
index 333dfea..3581dbb 100644
--- a/gcc/analyzer/sm-malloc.cc
+++ b/gcc/analyzer/sm-malloc.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3. If not see
#include "diagnostic-event-id.h"
#include "stringpool.h"
#include "attribs.h"
+#include "xml-printer.h"
#include "analyzer/analyzer-logging.h"
#include "analyzer/sm.h"
@@ -37,6 +38,7 @@ along with GCC; see the file COPYING3. If not see
#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
@@ -139,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);
@@ -290,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;
@@ -324,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)
@@ -334,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;
};
@@ -403,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;
@@ -429,6 +435,11 @@ public:
const svalue *new_ptr_sval,
const extrinsic_state &ext_state) const;
+ void
+ add_state_to_xml (xml_state &out_xml,
+ 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;
@@ -524,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))
{
}
@@ -568,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))
{
}
@@ -614,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
@@ -662,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);
}
@@ -808,11 +820,11 @@ public:
{
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 diagnostic_event::meaning (diagnostic_event::verb::acquire,
+ diagnostic_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 (diagnostic_event::verb::release,
+ diagnostic_event::noun::memory);
return diagnostic_event::meaning ();
}
@@ -1414,8 +1426,14 @@ private:
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"; }
@@ -1475,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;
+ std::unique_ptr<program_state> m_final_state;
};
class free_of_non_heap : public malloc_diagnostic
@@ -1571,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);
}
@@ -1798,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 ()
@@ -1828,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::
@@ -1837,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. */
@@ -1855,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. */
@@ -1886,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. */
@@ -1991,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. */
@@ -2172,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);
}
@@ -2376,12 +2409,12 @@ malloc_state_machine::handle_free_of_non_heap (sm_context &sm_ctxt,
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,
std::make_unique<free_of_non_heap>
@@ -2589,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 std::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
@@ -2624,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. */
@@ -2682,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
@@ -2697,7 +2732,31 @@ 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);
+}
+
+void
+malloc_state_machine::add_state_to_xml (xml_state &out_xml,
+ const svalue &sval,
+ state_machine::state_t state) const
+{
+ if (const region *reg = sval.maybe_get_region ())
+ {
+ auto &reg_element = out_xml.get_or_create_element (*reg);
+ auto alloc_state = as_a_allocation_state (state);
+ gcc_assert (alloc_state);
+
+ reg_element.set_attr ("dynamic-alloc-state", state->get_name ());
+ if (alloc_state->m_deallocators)
+ {
+ pretty_printer pp;
+ alloc_state->m_deallocators->dump_to_pp (&pp);
+ reg_element.set_attr ("expected-deallocators", pp_formatted_text (&pp));
+ }
+ if (alloc_state->m_deallocator)
+ reg_element.set_attr ("deallocator",
+ alloc_state->m_deallocator->m_name);
+ }
}
} // anonymous namespace