aboutsummaryrefslogtreecommitdiff
path: root/gcc/analyzer/sm-malloc.cc
diff options
context:
space:
mode:
authorbenjamin priour <priour.be@gmail.com>2023-09-01 00:01:29 +0200
committerbenjamin priour <vultkayn@gcc.gnu.org>2023-09-01 22:03:34 +0200
commite7b267444045c507654a2a3f758efee5d5b550df (patch)
tree1aa37e6cab63d98b2948bd00344fc93badc113b7 /gcc/analyzer/sm-malloc.cc
parentd3dd69706af4c086cb3385ff1f321887b91f49fb (diff)
downloadgcc-e7b267444045c507654a2a3f758efee5d5b550df.zip
gcc-e7b267444045c507654a2a3f758efee5d5b550df.tar.gz
gcc-e7b267444045c507654a2a3f758efee5d5b550df.tar.bz2
analyzer: Add support of placement new and improved operator new [PR105948,PR94355]
Fixed spurious possibly-NULL warning always tagging along throwing operator new despite it never returning NULL. Now operator new is correctly recognized as possibly returning NULL if and only if it is non-throwing or exceptions have been disabled. Different standard signatures of operator new are now properly recognized. Added support of placement new, so that it is now properly recognized, and a 'heap_allocated' region is no longer created for it. Placement new size is also checked and a 'Wanalyzer-allocation-size' is emitted when relevant, as well as always a 'Wanalyzer-out-of-bounds'. 'operator new' non-throwing variants are detected y checking the types of the parameters. Indeed, in a call to new (std::nothrow) () the chosen overload has signature 'operator new (void*, std::nothrow_t&)', where the second parameter is a reference. In a placement new, the second parameter will always be a void pointer. Prior to this patch, some buffers first allocated with 'new', then deleted an thereafter used would result in a 'Wanalyzer-user-after-free' warning. However the wording was "use after 'free'" instead of the expected "use after 'delete'". This patch fixes this by introducing a new kind of poisoned value, namely POISON_KIND_DELETED. Due to how the analyzer sees calls to non-throwing variants of operator new, dereferencing a pointer freshly allocated in this fashion caused both a 'Wanalyzer-use-of-uninitialized-value' and a 'Wanalyzer-null-dereference' to be emitted, while only the latter was relevant. As a result, 'null-dereference' now supersedes 'use-of-uninitialized'. Signed-off-by: benjamin priour <vultkayn@gcc.gnu.org> gcc/analyzer/ChangeLog: PR analyzer/105948 PR analyzer/94355 * analyzer.h (is_placement_new_p): New declaration. * call-details.cc (call_details::deref_ptr_arg): New function. Dereference the argument at given index if possible. * call-details.h: Declaration of the above function. * kf-lang-cp.cc (is_placement_new_p): Returns true if the gcall is recognized as a placement new. (kf_operator_delete::impl_call_post): Unbinding a region and its descendents now poisons with POISON_KIND_DELETED. (register_known_functions_lang_cp): Known function "operator delete" is now registered only once independently of its number of arguments. * region-model.cc (region_model::eval_condition): Now recursively calls itself if any of the operand is wrapped in a cast. * sm-malloc.cc (malloc_state_machine::on_stmt): Add placement new recognition. * svalue.cc (poison_kind_to_str): Wording for the new PK. * svalue.h (enum poison_kind): Add value POISON_KIND_DELETED. gcc/testsuite/ChangeLog: PR analyzer/105948 PR analyzer/94355 * g++.dg/analyzer/out-of-bounds-placement-new.C: Added a directive. * g++.dg/analyzer/placement-new.C: Added tests. * g++.dg/analyzer/new-2.C: New test. * g++.dg/analyzer/noexcept-new.C: New test. * g++.dg/analyzer/placement-new-size.C: New test.
Diffstat (limited to 'gcc/analyzer/sm-malloc.cc')
-rw-r--r--gcc/analyzer/sm-malloc.cc37
1 files changed, 30 insertions, 7 deletions
diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc
index 2ff777d..5af6544 100644
--- a/gcc/analyzer/sm-malloc.cc
+++ b/gcc/analyzer/sm-malloc.cc
@@ -759,7 +759,7 @@ public:
override
{
if (change.m_old_state == m_sm.get_start_state ()
- && unchecked_p (change.m_new_state))
+ && (unchecked_p (change.m_new_state) || nonnull_p (change.m_new_state)))
// TODO: verify that it's the allocation stmt, not a copy
return label_text::borrow ("allocated here");
if (unchecked_p (change.m_old_state)
@@ -1179,6 +1179,21 @@ public:
{
return ev.formatted_print ("dereference of NULL %qE", ev.m_expr);
}
+
+ /* Implementation of pending_diagnostic::supercedes_p for
+ null-deref.
+
+ We want null-deref to supercede use-of-unitialized-value,
+ so that if we have these at the same stmt, we don't emit
+ a use-of-uninitialized, just the null-deref. */
+
+ bool supercedes_p (const pending_diagnostic &other) const final override
+ {
+ if (other.use_of_uninit_p ())
+ return true;
+
+ return false;
+ }
};
/* Concrete subclass for describing passing a NULL value to a
@@ -1915,12 +1930,20 @@ malloc_state_machine::on_stmt (sm_context *sm_ctxt,
return true;
}
- if (is_named_call_p (callee_fndecl, "operator new", call, 1))
- on_allocator_call (sm_ctxt, call, &m_scalar_delete);
- else if (is_named_call_p (callee_fndecl, "operator new []", call, 1))
- on_allocator_call (sm_ctxt, call, &m_vector_delete);
- else if (is_named_call_p (callee_fndecl, "operator delete", call, 1)
- || is_named_call_p (callee_fndecl, "operator delete", call, 2))
+ if (!is_placement_new_p (call))
+ {
+ bool returns_nonnull = !TREE_NOTHROW (callee_fndecl)
+ && flag_exceptions;
+ if (is_named_call_p (callee_fndecl, "operator new"))
+ on_allocator_call (sm_ctxt, call,
+ &m_scalar_delete, returns_nonnull);
+ else if (is_named_call_p (callee_fndecl, "operator new []"))
+ on_allocator_call (sm_ctxt, call,
+ &m_vector_delete, returns_nonnull);
+ }
+
+ if (is_named_call_p (callee_fndecl, "operator delete", call, 1)
+ || is_named_call_p (callee_fndecl, "operator delete", call, 2))
{
on_deallocator_call (sm_ctxt, node, call,
&m_scalar_delete.m_deallocator, 0);