interesting_t interest;
pb.get_pending_diagnostic ()->mark_interesting_stuff (&interest);
+
+ /* Add region creation events for any globals of interest, at the
+ beginning of the path. */
+ {
+ for (auto reg : interest.m_region_creation)
+ switch (reg->get_memory_space ())
+ {
+ default:
+ continue;
+ case MEMSPACE_CODE:
+ case MEMSPACE_GLOBALS:
+ case MEMSPACE_READONLY_DATA:
+ {
+ const region *base_reg = reg->get_base_region ();
+ if (tree decl = base_reg->maybe_get_decl ())
+ if (DECL_P (decl)
+ && DECL_SOURCE_LOCATION (decl) != UNKNOWN_LOCATION)
+ {
+ emission_path->add_region_creation_event
+ (reg,
+ DECL_SOURCE_LOCATION (decl),
+ NULL_TREE,
+ 0);
+ }
+ }
+ }
+ }
+
+ /* Walk EPATH, adding events as appropriate. */
for (unsigned i = 0; i < epath.m_edges.length (); i++)
{
const exploded_edge *eedge = epath.m_edges[i];
return NULL_TREE;
}
+ const program_state *get_old_program_state () const FINAL OVERRIDE
+ {
+ return m_old_state;
+ }
+
const program_state *m_old_state;
const program_state *m_new_state;
const gimple *m_stmt;
break;
}
- /* Look for changes in dynamic extents, which will identify
- the creation of heap-based regions and alloca regions. */
- if (interest)
- {
- const region_model *src_model = src_state.m_region_model;
- const region_model *dst_model = dst_state.m_region_model;
- if (src_model->get_dynamic_extents ()
- != dst_model->get_dynamic_extents ())
- {
- unsigned i;
- const region *reg;
- FOR_EACH_VEC_ELT (interest->m_region_creation, i, reg)
- {
- const region *base_reg = reg->get_base_region ();
- const svalue *old_extents
- = src_model->get_dynamic_extents (base_reg);
- const svalue *new_extents
- = dst_model->get_dynamic_extents (base_reg);
- if (old_extents == NULL && new_extents != NULL)
- switch (base_reg->get_kind ())
- {
- default:
- break;
- case RK_HEAP_ALLOCATED:
- case RK_ALLOCA:
- emission_path->add_region_creation_event
- (reg,
- src_point.get_location (),
- src_point.get_fndecl (),
- src_stack_depth);
- break;
- }
- }
- }
- }
}
}
break;
}
+ /* Look for changes in dynamic extents, which will identify
+ the creation of heap-based regions and alloca regions. */
+ if (interest)
+ {
+ const region_model *src_model = src_state.m_region_model;
+ const region_model *dst_model = dst_state.m_region_model;
+ if (src_model->get_dynamic_extents ()
+ != dst_model->get_dynamic_extents ())
+ {
+ unsigned i;
+ const region *reg;
+ FOR_EACH_VEC_ELT (interest->m_region_creation, i, reg)
+ {
+ const region *base_reg = reg->get_base_region ();
+ const svalue *old_extents
+ = src_model->get_dynamic_extents (base_reg);
+ const svalue *new_extents
+ = dst_model->get_dynamic_extents (base_reg);
+ if (old_extents == NULL && new_extents != NULL)
+ switch (base_reg->get_kind ())
+ {
+ default:
+ break;
+ case RK_HEAP_ALLOCATED:
+ case RK_ALLOCA:
+ emission_path->add_region_creation_event
+ (reg,
+ src_point.get_location (),
+ src_point.get_fndecl (),
+ src_stack_depth);
+ break;
+ }
+ }
+ }
+ }
+
if (pb.get_feasibility_problem ()
&& &pb.get_feasibility_problem ()->m_eedge == &eedge)
{
return m_unknown_side_effects;
}
+ const program_state *get_old_program_state () const FINAL OVERRIDE
+ {
+ return m_old_state;
+ }
+
log_user m_logger;
exploded_graph &m_eg;
exploded_node *m_enode_for_diag;
if (const region_svalue *ptr = sval->dyn_cast_region_svalue ())
{
const region *reg = ptr->get_pointee ();
- const region *base_reg = reg->get_base_region ();
- if (base_reg->get_kind () == RK_DECL
- || base_reg->get_kind () == RK_STRING)
- return m_non_heap;
+ switch (reg->get_memory_space ())
+ {
+ default:
+ break;
+ case MEMSPACE_CODE:
+ case MEMSPACE_GLOBALS:
+ case MEMSPACE_STACK:
+ case MEMSPACE_READONLY_DATA:
+ return m_non_heap;
+ }
}
return m_start;
}
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,
+ tree arg,
+ const deallocator *d) const;
void on_deallocator_call (sm_context *sm_ctxt,
const supernode *node,
const gcall *call,
{
public:
free_of_non_heap (const malloc_state_machine &sm, tree arg,
+ const region *freed_reg,
const char *funcname)
- : malloc_diagnostic (sm, arg), m_funcname (funcname), m_kind (KIND_UNKNOWN)
+ : malloc_diagnostic (sm, arg), m_freed_reg (freed_reg), m_funcname (funcname)
{
}
FINAL OVERRIDE
{
const free_of_non_heap &other = (const free_of_non_heap &)base_other;
- return (same_tree_p (m_arg, other.m_arg) && m_kind == other.m_kind);
+ return (same_tree_p (m_arg, other.m_arg)
+ && m_freed_reg == other.m_freed_reg);
}
bool emit (rich_location *rich_loc) FINAL OVERRIDE
auto_diagnostic_group d;
diagnostic_metadata m;
m.add_cwe (590); /* CWE-590: Free of Memory not on the Heap. */
- switch (m_kind)
+ switch (get_memory_space ())
{
default:
+ case MEMSPACE_HEAP:
gcc_unreachable ();
- case KIND_UNKNOWN:
+ case MEMSPACE_UNKNOWN:
+ case MEMSPACE_CODE:
+ case MEMSPACE_GLOBALS:
+ case MEMSPACE_READONLY_DATA:
return warning_meta (rich_loc, m, OPT_Wanalyzer_free_of_non_heap,
"%<%s%> of %qE which points to memory"
" not on the heap",
m_funcname, m_arg);
break;
- case KIND_ALLOCA:
+ case MEMSPACE_STACK:
return warning_meta (rich_loc, m, OPT_Wanalyzer_free_of_non_heap,
- "%<%s%> of memory allocated on the stack by"
- " %qs (%qE) will corrupt the heap",
- m_funcname, "alloca", m_arg);
+ "%<%s%> of %qE which points to memory"
+ " on the stack",
+ m_funcname, m_arg);
break;
}
}
- label_text describe_state_change (const evdesc::state_change &change)
+ label_text describe_state_change (const evdesc::state_change &)
FINAL OVERRIDE
{
- /* Attempt to reconstruct what kind of pointer it is.
- (It seems neater for this to be a part of the state, though). */
- if (change.m_expr && TREE_CODE (change.m_expr) == SSA_NAME)
- {
- gimple *def_stmt = SSA_NAME_DEF_STMT (change.m_expr);
- if (gcall *call = dyn_cast <gcall *> (def_stmt))
- {
- if (is_special_named_call_p (call, "alloca", 1)
- || is_special_named_call_p (call, "__builtin_alloca", 1))
- {
- m_kind = KIND_ALLOCA;
- return label_text::borrow
- ("memory is allocated on the stack here");
- }
- }
- }
return label_text::borrow ("pointer is from here");
}
return ev.formatted_print ("call to %qs here", m_funcname);
}
+ void mark_interesting_stuff (interesting_t *interest) FINAL OVERRIDE
+ {
+ if (m_freed_reg)
+ interest->add_region_creation (m_freed_reg);
+ }
+
private:
- enum kind
+ enum memory_space get_memory_space () const
{
- KIND_UNKNOWN,
- KIND_ALLOCA
- };
+ if (m_freed_reg)
+ return m_freed_reg->get_memory_space ();
+ else
+ return MEMSPACE_UNKNOWN;
+ }
+
+ const region *m_freed_reg;
const char *m_funcname;
- enum kind m_kind;
};
/* struct allocation_state : public state_machine::state. */
if (any_pointer_p (lhs))
on_zero_assignment (sm_ctxt, stmt,lhs);
- /* If we have "LHS = &EXPR;" and EXPR is something other than a MEM_REF,
- transition LHS from start to non_heap.
- Doing it for ADDR_EXPR(MEM_REF()) is likely wrong, and can lead to
- unbounded chains of unmergeable sm-state on pointer arithmetic in loops
- when optimization is enabled. */
- if (const gassign *assign_stmt = dyn_cast <const gassign *> (stmt))
- {
- enum tree_code op = gimple_assign_rhs_code (assign_stmt);
- if (op == ADDR_EXPR)
- {
- tree lhs = gimple_assign_lhs (assign_stmt);
- if (lhs)
- {
- tree addr_expr = gimple_assign_rhs1 (assign_stmt);
- if (TREE_CODE (TREE_OPERAND (addr_expr, 0)) != MEM_REF)
- sm_ctxt->on_transition (node, stmt, lhs, m_start, m_non_heap);
- }
- }
- }
-
/* Handle dereferences. */
for (unsigned i = 0; i < gimple_num_ops (stmt); i++)
{
}
}
+/* Handle deallocations of non-heap pointers.
+ non-heap -> stop, with warning. */
+
+void
+malloc_state_machine::handle_free_of_non_heap (sm_context *sm_ctxt,
+ const supernode *node,
+ const gcall *call,
+ tree arg,
+ const deallocator *d) const
+{
+ tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
+ const region *freed_reg = NULL;
+ 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);
+ }
+ sm_ctxt->warn (node, call, arg,
+ new free_of_non_heap (*this, diag_arg, freed_reg,
+ d->m_name));
+ sm_ctxt->set_next_state (call, arg, m_stop);
+}
+
void
malloc_state_machine::on_deallocator_call (sm_context *sm_ctxt,
const supernode *node,
else if (state == m_non_heap)
{
/* non-heap -> stop, with warning. */
- tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
- sm_ctxt->warn (node, call, arg,
- new free_of_non_heap (*this, diag_arg,
- d->m_name));
- sm_ctxt->set_next_state (call, arg, m_stop);
+ handle_free_of_non_heap (sm_ctxt, node, call, arg, d);
}
}
else if (state == m_non_heap)
{
/* non-heap -> stop, with warning. */
- tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
- sm_ctxt->warn (node, call, arg,
- new free_of_non_heap (*this, diag_arg,
- d->m_name));
- sm_ctxt->set_next_state (call, arg, m_stop);
+ handle_free_of_non_heap (sm_ctxt, node, call, arg, d);
if (path_context *path_ctxt = sm_ctxt->get_path_context ())
path_ctxt->terminate_path ();
}
/* Are we handling an external function with unknown side effects? */
virtual bool unknown_side_effects_p () const { return false; }
+ virtual const program_state *get_old_program_state () const = 0;
+
+ const svalue *get_old_svalue (tree expr) const;
+
protected:
sm_context (int sm_idx, const state_machine &sm)
: m_sm_idx (sm_idx), m_sm (sm) {}
void test_3 (void)
{
- char buf[sizeof(int)];
+ char buf[sizeof(int)]; // { dg-message "region created on stack here" }
int *p = new(buf) int (42);
- delete p; // { dg-warning "memory not on the heap" }
+ delete p; // { dg-warning "memory on the stack" }
}
// { dg-prune-output "-Wfree-nonheap-object" }
int _M_single_bucket;
int *_M_buckets;
_Hashtable_alloc () { _M_buckets = &_M_single_bucket; }
- ~_Hashtable_alloc () { delete _M_buckets; } // { dg-warning "not on the heap" }
+ ~_Hashtable_alloc () { delete _M_buckets; } // { dg-warning "on the stack" }
};
void
void test_7 ()
{
struct foo f;
- foo_release (&f); /* { dg-warning "not on the heap" "analyzer" } */
+ foo_release (&f); /* { dg-warning "on the stack" "analyzer" } */
/* { dg-warning "'foo_release' called on unallocated object 'f'" "non-analyzer" { target *-*-* } .-1 } */
}
int test_24 (void)
{
- void *ptr = __builtin_alloca (sizeof (int)); /* { dg-message "memory is allocated on the stack here" } */
- free (ptr); /* { dg-warning "'free' of memory allocated on the stack by 'alloca' \\('ptr'\\) will corrupt the heap \\\[CWE-590\\\]" } */
+ void *ptr = __builtin_alloca (sizeof (int)); /* { dg-message "region created on stack here" } */
+ free (ptr); /* { dg-warning "'free' of 'ptr' which points to memory on the stack \\\[CWE-590\\\]" } */
}
int test_25 (void)
{
- char tmp[100];
- void *p = tmp; /* { dg-message "pointer is from here" } */
- free (p); /* { dg-warning "'free' of 'p' which points to memory not on the heap \\\[CWE-590\\\]" } */
+ char tmp[100]; /* { dg-message "region created on stack here" } */
+ void *p = tmp;
+ free (p); /* { dg-warning "'free' of 'p' which points to memory on the stack \\\[CWE-590\\\]" } */
/* TODO: more precise messages here. */
}
-char global_buffer[100];
+char global_buffer[100]; /* { dg-message "region created here" } */
int test_26 (void)
{
- void *p = global_buffer; /* { dg-message "pointer is from here" } */
+ void *p = global_buffer;
free (p); /* { dg-warning "'free' of 'p' which points to memory not on the heap \\\[CWE-590\\\]" } */
/* TODO: more precise messages here. */
}
return x;
}
+/* Free of function, and of label within function. */
+
+void test_50a (void)
+{
+}
+
+void test_50b (void)
+{
+ free (test_50a); /* { dg-warning "'free' of '&test_50a' which points to memory not on the heap \\\[CWE-590\\\]" } */
+}
+
+void test_50c (void)
+{
+ my_label:
+ free (&&my_label); /* { dg-warning "'free' of '&my_label' which points to memory not on the heap \\\[CWE-590\\\]" } */
+}
+
+
/* { dg-prune-output "\\\[-Wfree-nonheap-object" } */
{
allocator_t alloc_fn = get_alloca ();
deallocator_t dealloc_fn = get_free ();
- int *ptr = alloc_fn (sizeof (int)); /* { dg-message "pointer is from here" } */
- /* TODO: message should read "memory is allocated on the stack here". */
- dealloc_fn (ptr); /* { dg-warning "'free' of 'ptr' which points to memory not on the heap" } */
+ int *ptr = alloc_fn (sizeof (int)); /* dg-message "region created on stack here" } */
+ dealloc_fn (ptr); /* { dg-warning "'free' of 'ptr' which points to memory on the stack" } */
}
static void __attribute__((noinline))
{
void *ptr;
if (sz <= LIMIT)
- ptr = __builtin_alloca (sz); /* { dg-message "memory is allocated on the stack here" } */
+ ptr = __builtin_alloca (sz); /* { dg-message "region created on stack here" } */
else
ptr = malloc (sz);
/* Bug: the "sz <= LIMIT" above should have been "sz < LIMIT",
so there's a free-of-alloca when sz == LIMIT. */
if (sz >= LIMIT)
- free (ptr); /* { dg-warning "'free' of memory allocated on the stack by 'alloca'" } */
+ free (ptr); /* { dg-warning "'free' of 'ptr' which points to memory on the stack" } */
}
/* { dg-bogus "leak of 'ptr'" } */
/* This can't happen, as "sz > 1024" && "sz <= 1023" is impossible. */
--- /dev/null
+/* Reduced/adapted from false positive from -Wanalyzer-free-of-non-heap
+ seen on rdma-core. */
+
+#include <stddef.h>
+
+#define check_types_match(expr1, expr2) \
+ ((typeof(expr1) *)0 != (typeof(expr2) *)0)
+
+#define container_of(member_ptr, containing_type, member) \
+ ((containing_type *) \
+ ((char *)(member_ptr) \
+ - container_off(containing_type, member)) \
+ + check_types_match(*(member_ptr), ((containing_type *)0)->member))
+
+#define container_off(containing_type, member) \
+ offsetof(containing_type, member)
+
+struct ibv_device {
+ /* [...snip...] */
+};
+
+struct verbs_device {
+ struct ibv_device device; /* Must be first */
+ /* [...snip...] */
+ int placeholder;
+};
+
+struct mlx5_device {
+ struct verbs_device verbs_dev;
+ int placeholder;
+};
+
+static inline struct mlx5_device *to_mdev(struct ibv_device *ibdev)
+{
+ return container_of(ibdev, struct mlx5_device, verbs_dev.device);
+}
+
+static void mlx5_uninit_device(struct verbs_device *verbs_device)
+{
+ struct mlx5_device *dev = to_mdev(&verbs_device->device);
+
+ __builtin_free(dev); /* { dg-bogus "not on the heap" } */
+}
--- /dev/null
+struct ibv_device {
+ /* [...snip...] */
+ int placeholder;
+};
+
+struct verbs_device {
+ struct ibv_device device; /* Must be first */
+ /* [...snip...] */
+ int placeholder;
+};
+
+struct mlx5_device {
+ struct verbs_device verbs_dev;
+ int placeholder;
+};
+
+static inline struct mlx5_device *to_mdev(struct ibv_device *ibdev)
+{
+ return (struct mlx5_device *)ibdev;
+}
+
+static void mlx5_uninit_device(struct verbs_device *verbs_device)
+{
+ struct mlx5_device *dev = to_mdev(&verbs_device->device);
+ __builtin_free(dev); /* { dg-bogus "not on the heap" } */
+}
void *test_7 (size_t sz)
{
- char buf[100];
- void *p = realloc (&buf, sz); /* { dg-warning "'realloc' of '&buf' which points to memory not on the heap" } */
+ char buf[100]; /* { dg-message "region created on stack here" } */
+ void *p = realloc (&buf, sz); /* { dg-warning "'realloc' of '&buf' which points to memory on the stack" } */
return p;
}
s.b = 17;
__analyzer_eval (s.b == 17); /* { dg-warning "TRUE" } */
}
+
+void test_2 (int n)
+{
+ int arr[n]; /* { dg-message "region created on stack here" } */
+ __builtin_free (arr); /* { dg-warning "'free' of '<unknown>' which points to memory on the stack" } */
+ // TODO: fix the "unknown" here
+}
+
+/* { dg-prune-output "\\\[-Wfree-nonheap-object" } */