Add support for detecting mismatched allocation/deallocation calls.
PR c++/90629 - Support for -Wmismatched-new-delete PR middle-end/94527 - Add an __attribute__ that marks a function as freeing an object gcc/ChangeLog: PR c++/90629 PR middle-end/94527 * builtins.c (access_ref::access_ref): Initialize new member. (compute_objsize): Use access_ref::deref. Handle simple pointer assignment. (expand_builtin): Remove handling of the free built-in. (call_dealloc_argno): Same. (find_assignment_location): New function. (fndecl_alloc_p): Same. (gimple_call_alloc_p): Same. (call_dealloc_p): Same. (matching_alloc_calls_p): Same. (warn_dealloc_offset): Same. (maybe_emit_free_warning): Same. * builtins.h (struct access_ref): Declare new member. (maybe_emit_free_warning): Make extern. Make use of access_ref. Handle -Wmismatched-new-delete. * calls.c (initialize_argument_information): Call maybe_emit_free_warning. * doc/extend.texi (attribute malloc): Update. * doc/invoke.texi (-Wfree-nonheap-object): Expand documentation. (-Wmismatched-new-delete): Document new option. (-Wmismatched-dealloc): Document new option. gcc/c-family/ChangeLog: PR c++/90629 PR middle-end/94527 * c-attribs.c (handle_dealloc_attribute): New function. (handle_malloc_attribute): Handle argument forms of attribute. * c.opt (-Wmismatched-dealloc): New option. (-Wmismatched-new-delete): New option. gcc/testsuite/ChangeLog: PR c++/90629 PR middle-end/94527 * g++.dg/asan/asan_test.cc: Fix a bug. * g++.dg/warn/delete-array-1.C: Add expected warning. * g++.old-deja/g++.other/delete2.C: Add expected warning. * g++.dg/warn/Wfree-nonheap-object-2.C: New test. * g++.dg/warn/Wfree-nonheap-object.C: New test. * g++.dg/warn/Wmismatched-new-delete.C: New test. * g++.dg/warn/Wmismatched-dealloc-2.C: New test. * g++.dg/warn/Wmismatched-dealloc.C: New test. * gcc.dg/Wmismatched-dealloc.c: New test. * gcc.dg/analyzer/malloc-1.c: Prune out expected warning. * gcc.dg/attr-malloc.c: New test. * gcc.dg/free-1.c: Adjust text of expected warning. * gcc.dg/free-2.c: Same. * gcc.dg/torture/pr71816.c: Prune out expected warning. * gcc.dg/tree-ssa/pr19831-2.c: Add an expected warning. * gcc.dg/Wfree-nonheap-object-2.c: New test. * gcc.dg/Wfree-nonheap-object-3.c: New test. * gcc.dg/Wfree-nonheap-object.c: New test. libstdc++-v3/ChangeLog: * testsuite/ext/vstring/modifiers/clear/56166.cc: Suppress a false positive warning.
This commit is contained in:
parent
a3f7a6957a
commit
dce6c58db8
27 changed files with 2531 additions and 70 deletions
425
gcc/builtins.c
425
gcc/builtins.c
|
@ -73,6 +73,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "gomp-constants.h"
|
||||
#include "omp-general.h"
|
||||
#include "tree-dfa.h"
|
||||
#include "gimple-iterator.h"
|
||||
#include "gimple-ssa.h"
|
||||
#include "tree-ssa-live.h"
|
||||
#include "tree-outof-ssa.h"
|
||||
|
@ -182,7 +183,6 @@ static rtx expand_builtin_memory_chk (tree, rtx, machine_mode,
|
|||
enum built_in_function);
|
||||
static void maybe_emit_chk_warning (tree, enum built_in_function);
|
||||
static void maybe_emit_sprintf_chk_warning (tree, enum built_in_function);
|
||||
static void maybe_emit_free_warning (tree);
|
||||
static tree fold_builtin_object_size (tree, tree);
|
||||
static bool check_read_access (tree, tree, tree = NULL_TREE, int = 1);
|
||||
static bool compute_objsize_r (tree, int, access_ref *, ssa_name_limit_t &,
|
||||
|
@ -201,8 +201,8 @@ static void expand_builtin_sync_synchronize (void);
|
|||
|
||||
access_ref::access_ref (tree bound /* = NULL_TREE */,
|
||||
bool minaccess /* = false */)
|
||||
: ref (), eval ([](tree x){ return x; }), trail1special (true), base0 (true),
|
||||
parmarray ()
|
||||
: ref (), eval ([](tree x){ return x; }), deref (), trail1special (true),
|
||||
base0 (true), parmarray ()
|
||||
{
|
||||
/* Set to valid. */
|
||||
offrng[0] = offrng[1] = 0;
|
||||
|
@ -5313,7 +5313,10 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
|
|||
|
||||
const bool addr = TREE_CODE (ptr) == ADDR_EXPR;
|
||||
if (addr)
|
||||
ptr = TREE_OPERAND (ptr, 0);
|
||||
{
|
||||
--pref->deref;
|
||||
ptr = TREE_OPERAND (ptr, 0);
|
||||
}
|
||||
|
||||
if (DECL_P (ptr))
|
||||
{
|
||||
|
@ -5421,6 +5424,8 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
|
|||
|
||||
if (code == ARRAY_REF || code == MEM_REF)
|
||||
{
|
||||
++pref->deref;
|
||||
|
||||
tree ref = TREE_OPERAND (ptr, 0);
|
||||
tree reftype = TREE_TYPE (ref);
|
||||
if (!addr && code == ARRAY_REF
|
||||
|
@ -5544,6 +5549,10 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
|
|||
if (!compute_objsize_r (ref, ostype, pref, snlim, qry))
|
||||
return false;
|
||||
|
||||
/* Clear DEREF since the offset is being applied to the target
|
||||
of the dereference. */
|
||||
pref->deref = 0;
|
||||
|
||||
offset_int orng[2];
|
||||
tree off = pref->eval (TREE_OPERAND (ptr, 1));
|
||||
if (get_offset_range (off, NULL, orng, rvals))
|
||||
|
@ -10630,11 +10639,6 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
|
|||
maybe_emit_sprintf_chk_warning (exp, fcode);
|
||||
break;
|
||||
|
||||
case BUILT_IN_FREE:
|
||||
if (warn_free_nonheap_object)
|
||||
maybe_emit_free_warning (exp);
|
||||
break;
|
||||
|
||||
case BUILT_IN_THREAD_POINTER:
|
||||
return expand_builtin_thread_pointer (exp, target);
|
||||
|
||||
|
@ -12944,30 +12948,403 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
|
|||
access_write_only);
|
||||
}
|
||||
|
||||
/* Emit warning if a free is called with address of a variable. */
|
||||
/* Return true if STMT is a call to an allocation function. Unless
|
||||
ALL_ALLOC is set, consider only functions that return dynmamically
|
||||
allocated objects. Otherwise return true even for all forms of
|
||||
alloca (including VLA). */
|
||||
|
||||
static void
|
||||
static bool
|
||||
fndecl_alloc_p (tree fndecl, bool all_alloc)
|
||||
{
|
||||
if (!fndecl)
|
||||
return false;
|
||||
|
||||
/* A call to operator new isn't recognized as one to a built-in. */
|
||||
if (DECL_IS_OPERATOR_NEW_P (fndecl))
|
||||
return true;
|
||||
|
||||
if (fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
|
||||
{
|
||||
switch (DECL_FUNCTION_CODE (fndecl))
|
||||
{
|
||||
case BUILT_IN_ALLOCA:
|
||||
case BUILT_IN_ALLOCA_WITH_ALIGN:
|
||||
return all_alloc;
|
||||
case BUILT_IN_CALLOC:
|
||||
case BUILT_IN_MALLOC:
|
||||
case BUILT_IN_REALLOC:
|
||||
case BUILT_IN_STRDUP:
|
||||
case BUILT_IN_STRNDUP:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* A function is considered an allocation function if it's declared
|
||||
with attribute malloc with an argument naming its associated
|
||||
deallocation function. */
|
||||
tree attrs = DECL_ATTRIBUTES (fndecl);
|
||||
if (!attrs)
|
||||
return false;
|
||||
|
||||
for (tree allocs = attrs;
|
||||
(allocs = lookup_attribute ("malloc", allocs));
|
||||
allocs = TREE_CHAIN (allocs))
|
||||
{
|
||||
tree args = TREE_VALUE (allocs);
|
||||
if (!args)
|
||||
continue;
|
||||
|
||||
if (TREE_VALUE (args))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return true if STMT is a call to an allocation function. A wrapper
|
||||
around fndecl_alloc_p. */
|
||||
|
||||
static bool
|
||||
gimple_call_alloc_p (gimple *stmt, bool all_alloc = false)
|
||||
{
|
||||
return fndecl_alloc_p (gimple_call_fndecl (stmt), all_alloc);
|
||||
}
|
||||
|
||||
/* Return the zero-based number corresponding to the argument being
|
||||
deallocated if STMT is a call to a deallocation function or UINT_MAX
|
||||
if it isn't. */
|
||||
|
||||
static unsigned
|
||||
call_dealloc_argno (tree exp)
|
||||
{
|
||||
tree fndecl = get_callee_fndecl (exp);
|
||||
if (!fndecl)
|
||||
return UINT_MAX;
|
||||
|
||||
/* A call to operator delete isn't recognized as one to a built-in. */
|
||||
if (DECL_IS_OPERATOR_DELETE_P (fndecl))
|
||||
return 0;
|
||||
|
||||
/* TODO: Handle user-defined functions with attribute malloc? Handle
|
||||
known non-built-ins like fopen? */
|
||||
if (fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
|
||||
{
|
||||
switch (DECL_FUNCTION_CODE (fndecl))
|
||||
{
|
||||
case BUILT_IN_FREE:
|
||||
case BUILT_IN_REALLOC:
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return UINT_MAX;
|
||||
}
|
||||
|
||||
tree attrs = DECL_ATTRIBUTES (fndecl);
|
||||
if (!attrs)
|
||||
return UINT_MAX;
|
||||
|
||||
for (tree atfree = attrs;
|
||||
(atfree = lookup_attribute ("*dealloc", atfree));
|
||||
atfree = TREE_CHAIN (atfree))
|
||||
{
|
||||
tree alloc = TREE_VALUE (atfree);
|
||||
if (!alloc)
|
||||
continue;
|
||||
|
||||
tree pos = TREE_CHAIN (alloc);
|
||||
if (!pos)
|
||||
return 0;
|
||||
|
||||
pos = TREE_VALUE (pos);
|
||||
return TREE_INT_CST_LOW (pos) - 1;
|
||||
}
|
||||
|
||||
return UINT_MAX;
|
||||
}
|
||||
|
||||
/* Return true if STMT is a call to a deallocation function. */
|
||||
|
||||
static inline bool
|
||||
call_dealloc_p (tree exp)
|
||||
{
|
||||
return call_dealloc_argno (exp) != UINT_MAX;
|
||||
}
|
||||
|
||||
/* ALLOC_DECL and DEALLOC_DECL are pair of allocation and deallocation
|
||||
functions. Return true if the latter is suitable to deallocate objects
|
||||
allocated by calls to the former. */
|
||||
|
||||
static bool
|
||||
matching_alloc_calls_p (tree alloc_decl, tree dealloc_decl)
|
||||
{
|
||||
if (DECL_IS_OPERATOR_NEW_P (alloc_decl))
|
||||
{
|
||||
if (DECL_IS_OPERATOR_DELETE_P (dealloc_decl))
|
||||
{
|
||||
/* Return true iff both functions are of the same array or
|
||||
singleton form and false otherwise. */
|
||||
tree alloc_id = DECL_NAME (alloc_decl);
|
||||
tree dealloc_id = DECL_NAME (dealloc_decl);
|
||||
const char *alloc_fname = IDENTIFIER_POINTER (alloc_id);
|
||||
const char *dealloc_fname = IDENTIFIER_POINTER (dealloc_id);
|
||||
return !strchr (alloc_fname, '[') == !strchr (dealloc_fname, '[');
|
||||
}
|
||||
|
||||
/* Return false for deallocation functions that are known not
|
||||
to match. */
|
||||
if (fndecl_built_in_p (dealloc_decl, BUILT_IN_FREE)
|
||||
|| fndecl_built_in_p (dealloc_decl, BUILT_IN_REALLOC))
|
||||
return false;
|
||||
/* Otherwise proceed below to check the deallocation function's
|
||||
"*dealloc" attributes to look for one that mentions this operator
|
||||
new. */
|
||||
}
|
||||
else if (fndecl_built_in_p (alloc_decl, BUILT_IN_NORMAL))
|
||||
{
|
||||
switch (DECL_FUNCTION_CODE (alloc_decl))
|
||||
{
|
||||
case BUILT_IN_ALLOCA:
|
||||
case BUILT_IN_ALLOCA_WITH_ALIGN:
|
||||
return false;
|
||||
|
||||
case BUILT_IN_CALLOC:
|
||||
case BUILT_IN_MALLOC:
|
||||
case BUILT_IN_REALLOC:
|
||||
case BUILT_IN_STRDUP:
|
||||
case BUILT_IN_STRNDUP:
|
||||
if (DECL_IS_OPERATOR_DELETE_P (dealloc_decl))
|
||||
return false;
|
||||
|
||||
if (fndecl_built_in_p (dealloc_decl, BUILT_IN_FREE)
|
||||
|| fndecl_built_in_p (dealloc_decl, BUILT_IN_REALLOC))
|
||||
return true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If DEALLOC_DECL has internal "*dealloc" attribute scan the list of
|
||||
its associated allocation functions for ALLOC_DECL. If it's found
|
||||
they are a matching pair, otherwise they're not. */
|
||||
tree attrs = DECL_ATTRIBUTES (dealloc_decl);
|
||||
if (!attrs)
|
||||
return false;
|
||||
|
||||
for (tree funs = attrs;
|
||||
(funs = lookup_attribute ("*dealloc", funs));
|
||||
funs = TREE_CHAIN (funs))
|
||||
{
|
||||
tree args = TREE_VALUE (funs);
|
||||
if (!args)
|
||||
continue;
|
||||
|
||||
tree fname = TREE_VALUE (args);
|
||||
if (!fname)
|
||||
continue;
|
||||
|
||||
if (fname == DECL_NAME (alloc_decl))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return true if DEALLOC_DECL is a function suitable to deallocate
|
||||
objectes allocated by the ALLOC call. */
|
||||
|
||||
static bool
|
||||
matching_alloc_calls_p (gimple *alloc, tree dealloc_decl)
|
||||
{
|
||||
tree alloc_decl = gimple_call_fndecl (alloc);
|
||||
if (!alloc_decl)
|
||||
return true;
|
||||
|
||||
return matching_alloc_calls_p (alloc_decl, dealloc_decl);
|
||||
}
|
||||
|
||||
/* Diagnose a call to FNDECL to deallocate a pointer referenced by
|
||||
AREF that includes a nonzero offset. Such a pointer cannot refer
|
||||
to the beginning of an allocated object. A negative offset may
|
||||
refer to it only if the target pointer is unknown. */
|
||||
|
||||
static bool
|
||||
warn_dealloc_offset (location_t loc, tree exp, tree fndecl,
|
||||
const access_ref &aref)
|
||||
{
|
||||
char offstr[80];
|
||||
offstr[0] = '\0';
|
||||
if (wi::fits_shwi_p (aref.offrng[0]))
|
||||
{
|
||||
if (aref.offrng[0] == aref.offrng[1]
|
||||
|| !wi::fits_shwi_p (aref.offrng[1]))
|
||||
sprintf (offstr, " %lli",
|
||||
(long long)aref.offrng[0].to_shwi ());
|
||||
else
|
||||
sprintf (offstr, " [%lli, %lli]",
|
||||
(long long)aref.offrng[0].to_shwi (),
|
||||
(long long)aref.offrng[1].to_shwi ());
|
||||
}
|
||||
|
||||
if (!warning_at (loc, OPT_Wfree_nonheap_object,
|
||||
"%K%qD called on pointer %qE with nonzero offset%s",
|
||||
exp, fndecl, aref.ref, offstr))
|
||||
return false;
|
||||
|
||||
if (DECL_P (aref.ref))
|
||||
inform (DECL_SOURCE_LOCATION (aref.ref), "declared here");
|
||||
else if (TREE_CODE (aref.ref) == SSA_NAME)
|
||||
{
|
||||
gimple *def_stmt = SSA_NAME_DEF_STMT (aref.ref);
|
||||
if (is_gimple_call (def_stmt))
|
||||
{
|
||||
tree alloc_decl = gimple_call_fndecl (def_stmt);
|
||||
inform (gimple_location (def_stmt),
|
||||
"returned from a call to %qD", alloc_decl);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Issue a warning if a deallocation function such as free, realloc,
|
||||
or C++ operator delete is called with an argument not returned by
|
||||
a matching allocation function such as malloc or the corresponding
|
||||
form of C++ operatorn new. */
|
||||
|
||||
void
|
||||
maybe_emit_free_warning (tree exp)
|
||||
{
|
||||
if (call_expr_nargs (exp) != 1)
|
||||
tree fndecl = get_callee_fndecl (exp);
|
||||
if (!fndecl)
|
||||
return;
|
||||
|
||||
tree arg = CALL_EXPR_ARG (exp, 0);
|
||||
|
||||
STRIP_NOPS (arg);
|
||||
if (TREE_CODE (arg) != ADDR_EXPR)
|
||||
unsigned argno = call_dealloc_argno (exp);
|
||||
if ((unsigned) call_expr_nargs (exp) <= argno)
|
||||
return;
|
||||
|
||||
arg = get_base_address (TREE_OPERAND (arg, 0));
|
||||
if (arg == NULL || INDIRECT_REF_P (arg) || TREE_CODE (arg) == MEM_REF)
|
||||
tree ptr = CALL_EXPR_ARG (exp, argno);
|
||||
if (integer_zerop (ptr))
|
||||
return;
|
||||
|
||||
if (SSA_VAR_P (arg))
|
||||
warning_at (tree_nonartificial_location (exp), OPT_Wfree_nonheap_object,
|
||||
"%Kattempt to free a non-heap object %qD", exp, arg);
|
||||
else
|
||||
warning_at (tree_nonartificial_location (exp), OPT_Wfree_nonheap_object,
|
||||
"%Kattempt to free a non-heap object", exp);
|
||||
access_ref aref;
|
||||
if (!compute_objsize (ptr, 0, &aref))
|
||||
return;
|
||||
|
||||
tree ref = aref.ref;
|
||||
if (integer_zerop (ref))
|
||||
return;
|
||||
|
||||
tree dealloc_decl = get_callee_fndecl (exp);
|
||||
location_t loc = tree_nonartificial_location (exp);
|
||||
loc = expansion_point_location_if_in_system_header (loc);
|
||||
|
||||
if (DECL_P (ref) || EXPR_P (ref))
|
||||
{
|
||||
/* Diagnose freeing a declared object. */
|
||||
if (aref.ref_declared ()
|
||||
&& warning_at (loc, OPT_Wfree_nonheap_object,
|
||||
"%K%qD called on unallocated object %qD",
|
||||
exp, dealloc_decl, ref))
|
||||
{
|
||||
inform (DECL_SOURCE_LOCATION (ref),
|
||||
"declared here");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Diagnose freeing a pointer that includes a positive offset.
|
||||
Such a pointer cannot refer to the beginning of an allocated
|
||||
object. A negative offset may refer to it. */
|
||||
if (!aref.deref
|
||||
&& aref.sizrng[0] != aref.sizrng[1]
|
||||
&& aref.offrng[0] > 0 && aref.offrng[1] > 0
|
||||
&& warn_dealloc_offset (loc, exp, dealloc_decl, aref))
|
||||
return;
|
||||
}
|
||||
else if (CONSTANT_CLASS_P (ref))
|
||||
{
|
||||
if (warning_at (loc, OPT_Wfree_nonheap_object,
|
||||
"%K%qD called on a pointer to an unallocated "
|
||||
"object %qE", exp, dealloc_decl, ref))
|
||||
{
|
||||
if (TREE_CODE (ptr) == SSA_NAME)
|
||||
{
|
||||
gimple *def_stmt = SSA_NAME_DEF_STMT (ptr);
|
||||
if (is_gimple_assign (def_stmt))
|
||||
{
|
||||
location_t loc = gimple_location (def_stmt);
|
||||
inform (loc, "assigned here");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (TREE_CODE (ref) == SSA_NAME)
|
||||
{
|
||||
/* Also warn if the pointer argument refers to the result
|
||||
of an allocation call like alloca or VLA. */
|
||||
gimple *def_stmt = SSA_NAME_DEF_STMT (ref);
|
||||
if (is_gimple_call (def_stmt))
|
||||
{
|
||||
bool warned = false;
|
||||
if (gimple_call_alloc_p (def_stmt))
|
||||
{
|
||||
if (matching_alloc_calls_p (def_stmt, dealloc_decl))
|
||||
{
|
||||
if (!aref.deref
|
||||
&& aref.offrng[0] > 0 && aref.offrng[1] > 0
|
||||
&& warn_dealloc_offset (loc, exp, dealloc_decl, aref))
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
tree alloc_decl = gimple_call_fndecl (def_stmt);
|
||||
int opt = (DECL_IS_OPERATOR_NEW_P (alloc_decl)
|
||||
|| DECL_IS_OPERATOR_DELETE_P (dealloc_decl)
|
||||
? OPT_Wmismatched_new_delete
|
||||
: OPT_Wmismatched_dealloc);
|
||||
warned = warning_at (loc, opt,
|
||||
"%K%qD called on pointer returned "
|
||||
"from a mismatched allocation "
|
||||
"function", exp, dealloc_decl);
|
||||
}
|
||||
}
|
||||
else if (gimple_call_builtin_p (def_stmt, BUILT_IN_ALLOCA)
|
||||
|| gimple_call_builtin_p (def_stmt,
|
||||
BUILT_IN_ALLOCA_WITH_ALIGN))
|
||||
warned = warning_at (loc, OPT_Wfree_nonheap_object,
|
||||
"%K%qD called on pointer to "
|
||||
"an unallocated object",
|
||||
exp, dealloc_decl);
|
||||
else if (!aref.deref
|
||||
&& aref.offrng[0] > 0 && aref.offrng[1] > 0
|
||||
&& warn_dealloc_offset (loc, exp, dealloc_decl, aref))
|
||||
return;
|
||||
|
||||
if (warned)
|
||||
{
|
||||
tree fndecl = gimple_call_fndecl (def_stmt);
|
||||
inform (gimple_location (def_stmt),
|
||||
"returned from a call to %qD", fndecl);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (gimple_nop_p (def_stmt))
|
||||
{
|
||||
ref = SSA_NAME_VAR (ref);
|
||||
/* Diagnose freeing a pointer that includes a positive offset. */
|
||||
if (TREE_CODE (ref) == PARM_DECL
|
||||
&& !aref.deref
|
||||
&& aref.sizrng[0] != aref.sizrng[1]
|
||||
&& aref.offrng[0] > 0 && aref.offrng[1] > 0
|
||||
&& warn_dealloc_offset (loc, exp, dealloc_decl, aref))
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Fold a call to __builtin_object_size with arguments PTR and OST,
|
||||
|
|
|
@ -220,6 +220,12 @@ struct access_ref
|
|||
argument to the minimum. */
|
||||
offset_int size_remaining (offset_int * = NULL) const;
|
||||
|
||||
/* Return true if *THIS is an access to a declared object. */
|
||||
bool ref_declared () const
|
||||
{
|
||||
return DECL_P (ref) && base0 && deref < 1;
|
||||
}
|
||||
|
||||
/* Set the size range to the maximum. */
|
||||
void set_max_size_range ()
|
||||
{
|
||||
|
@ -261,6 +267,9 @@ struct access_ref
|
|||
|
||||
/* Used to fold integer expressions when called from front ends. */
|
||||
tree (*eval)(tree);
|
||||
/* Positive when REF is dereferenced, negative when its address is
|
||||
taken. */
|
||||
int deref;
|
||||
/* Set if trailing one-element arrays should be treated as flexible
|
||||
array members. */
|
||||
bool trail1special;
|
||||
|
@ -350,5 +359,6 @@ extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL,
|
|||
range_query * = NULL);
|
||||
extern bool check_access (tree, tree, tree, tree, tree,
|
||||
access_mode, const access_data * = NULL);
|
||||
extern void maybe_emit_free_warning (tree);
|
||||
|
||||
#endif /* GCC_BUILTINS_H */
|
||||
|
|
|
@ -113,6 +113,7 @@ static tree handle_no_instrument_function_attribute (tree *, tree,
|
|||
static tree handle_no_profile_instrument_function_attribute (tree *, tree,
|
||||
tree, int, bool *);
|
||||
static tree handle_malloc_attribute (tree *, tree, tree, int, bool *);
|
||||
static tree handle_dealloc_attribute (tree *, tree, tree, int, bool *);
|
||||
static tree handle_returns_twice_attribute (tree *, tree, tree, int, bool *);
|
||||
static tree handle_no_limit_stack_attribute (tree *, tree, tree, int,
|
||||
bool *);
|
||||
|
@ -364,7 +365,7 @@ const struct attribute_spec c_common_attribute_table[] =
|
|||
{ "no_profile_instrument_function", 0, 0, true, false, false, false,
|
||||
handle_no_profile_instrument_function_attribute,
|
||||
NULL },
|
||||
{ "malloc", 0, 0, true, false, false, false,
|
||||
{ "malloc", 0, 2, true, false, false, false,
|
||||
handle_malloc_attribute, attr_alloc_exclusions },
|
||||
{ "returns_twice", 0, 0, true, false, false, false,
|
||||
handle_returns_twice_attribute,
|
||||
|
@ -524,6 +525,8 @@ const struct attribute_spec c_common_attribute_table[] =
|
|||
handle_objc_root_class_attribute, NULL },
|
||||
{ "objc_nullability", 1, 1, true, false, false, false,
|
||||
handle_objc_nullability_attribute, NULL },
|
||||
{ "*dealloc", 1, 2, true, false, false, false,
|
||||
handle_dealloc_attribute, NULL },
|
||||
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
|
||||
};
|
||||
|
||||
|
@ -3127,20 +3130,179 @@ handle_no_profile_instrument_function_attribute (tree *node, tree name, tree,
|
|||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Handle a "malloc" attribute; arguments as in
|
||||
struct attribute_spec.handler. */
|
||||
/* Handle the "malloc" attribute. */
|
||||
|
||||
static tree
|
||||
handle_malloc_attribute (tree *node, tree name, tree ARG_UNUSED (args),
|
||||
handle_malloc_attribute (tree *node, tree name, tree args,
|
||||
int ARG_UNUSED (flags), bool *no_add_attrs)
|
||||
{
|
||||
if (TREE_CODE (*node) == FUNCTION_DECL
|
||||
&& POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (*node))))
|
||||
DECL_IS_MALLOC (*node) = 1;
|
||||
else
|
||||
tree fndecl = *node;
|
||||
|
||||
if (TREE_CODE (*node) != FUNCTION_DECL)
|
||||
{
|
||||
warning (OPT_Wattributes, "%qE attribute ignored", name);
|
||||
warning (OPT_Wattributes, "%qE attribute ignored; valid only "
|
||||
"for functions",
|
||||
name);
|
||||
*no_add_attrs = true;
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
tree rettype = TREE_TYPE (TREE_TYPE (*node));
|
||||
if (!POINTER_TYPE_P (rettype))
|
||||
{
|
||||
warning (OPT_Wattributes, "%qE attribute ignored on functions "
|
||||
"returning %qT; valid only for pointer return types",
|
||||
name, rettype);
|
||||
*no_add_attrs = true;
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
if (!args)
|
||||
{
|
||||
/* Only the form of the attribute with no arguments declares
|
||||
a function malloc-like. */
|
||||
DECL_IS_MALLOC (*node) = 1;
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
tree dealloc = TREE_VALUE (args);
|
||||
if (error_operand_p (dealloc))
|
||||
{
|
||||
/* If the argument is in error it will have already been diagnosed.
|
||||
Avoid issuing redundant errors here. */
|
||||
*no_add_attrs = true;
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* In C++ the argument may be wrapped in a cast to disambiguate one
|
||||
of a number of overloads (such as operator delete). Strip it. */
|
||||
STRIP_NOPS (dealloc);
|
||||
if (TREE_CODE (dealloc) == ADDR_EXPR)
|
||||
dealloc = TREE_OPERAND (dealloc, 0);
|
||||
|
||||
if (TREE_CODE (dealloc) != FUNCTION_DECL)
|
||||
{
|
||||
if (TREE_CODE (dealloc) == OVERLOAD)
|
||||
{
|
||||
/* Handle specially the common case of specifying one of a number
|
||||
of overloads, such as operator delete. */
|
||||
error ("%qE attribute argument 1 is ambiguous", name);
|
||||
inform (input_location,
|
||||
"use a cast to the expected type to disambiguate");
|
||||
*no_add_attrs = true;
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
error ("%qE attribute argument 1 does not name a function", name);
|
||||
if (DECL_P (dealloc))
|
||||
inform (DECL_SOURCE_LOCATION (dealloc),
|
||||
"argument references a symbol declared here");
|
||||
*no_add_attrs = true;
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Mentioning the deallocation function qualifies as its use. */
|
||||
TREE_USED (dealloc) = 1;
|
||||
|
||||
tree fntype = TREE_TYPE (dealloc);
|
||||
tree argpos = TREE_CHAIN (args) ? TREE_VALUE (TREE_CHAIN (args)) : NULL_TREE;
|
||||
if (!argpos)
|
||||
{
|
||||
tree argtypes = TYPE_ARG_TYPES (fntype);
|
||||
if (!argtypes)
|
||||
{
|
||||
/* Reject functions without a prototype. */
|
||||
error ("%qE attribute argument 1 must take a pointer "
|
||||
"type as its first argument", name);
|
||||
inform (DECL_SOURCE_LOCATION (dealloc),
|
||||
"refernced symbol declared here" );
|
||||
*no_add_attrs = true;
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
tree argtype = TREE_VALUE (argtypes);
|
||||
if (TREE_CODE (argtype) != POINTER_TYPE)
|
||||
{
|
||||
/* Reject functions that don't take a pointer as their first
|
||||
argument. */
|
||||
error ("%qE attribute argument 1 must take a pointer type "
|
||||
"as its first argument; have %qT", name, argtype);
|
||||
inform (DECL_SOURCE_LOCATION (dealloc),
|
||||
"referenced symbol declared here" );
|
||||
*no_add_attrs = true;
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
*no_add_attrs = false;
|
||||
tree attr_free = build_tree_list (NULL_TREE, DECL_NAME (fndecl));
|
||||
attr_free = build_tree_list (get_identifier ("*dealloc"), attr_free);
|
||||
decl_attributes (&dealloc, attr_free, 0);
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Validate the positional argument. */
|
||||
argpos = positional_argument (fntype, name, argpos, POINTER_TYPE);
|
||||
if (!argpos)
|
||||
{
|
||||
*no_add_attrs = true;
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* It's valid to declare the same function with multiple instances
|
||||
of attribute malloc, each naming the same or different deallocator
|
||||
functions, and each referencing either the same or a different
|
||||
positional argument. */
|
||||
*no_add_attrs = false;
|
||||
tree attr_free = tree_cons (NULL_TREE, argpos, NULL_TREE);
|
||||
attr_free = tree_cons (NULL_TREE, DECL_NAME (fndecl), attr_free);
|
||||
attr_free = build_tree_list (get_identifier ("*dealloc"), attr_free);
|
||||
decl_attributes (&dealloc, attr_free, 0);
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Handle the internal "*dealloc" attribute added for functions declared
|
||||
with the one- and two-argument forms of attribute malloc. Add it
|
||||
to *NODE unless it's already there with the same arguments. */
|
||||
|
||||
static tree
|
||||
handle_dealloc_attribute (tree *node, tree name, tree args, int,
|
||||
bool *no_add_attrs)
|
||||
{
|
||||
tree fndecl = *node;
|
||||
|
||||
tree attrs = DECL_ATTRIBUTES (fndecl);
|
||||
if (!attrs)
|
||||
return NULL_TREE;
|
||||
|
||||
tree arg_fname = TREE_VALUE (args);
|
||||
args = TREE_CHAIN (args);
|
||||
tree arg_pos = args ? TREE_VALUE (args) : NULL_TREE;
|
||||
|
||||
gcc_checking_assert (TREE_CODE (arg_fname) == IDENTIFIER_NODE);
|
||||
|
||||
const char* const namestr = IDENTIFIER_POINTER (name);
|
||||
for (tree at = attrs; (at = lookup_attribute (namestr, at));
|
||||
at = TREE_CHAIN (at))
|
||||
{
|
||||
tree alloc = TREE_VALUE (at);
|
||||
if (!alloc)
|
||||
continue;
|
||||
|
||||
tree pos = TREE_CHAIN (alloc);
|
||||
alloc = TREE_VALUE (alloc);
|
||||
pos = pos ? TREE_VALUE (pos) : NULL_TREE;
|
||||
gcc_checking_assert (TREE_CODE (alloc) == IDENTIFIER_NODE);
|
||||
|
||||
if (alloc == arg_fname
|
||||
&& ((!pos && !arg_pos)
|
||||
|| (pos && arg_pos && tree_int_cst_equal (pos, arg_pos))))
|
||||
{
|
||||
/* The function already has the attribute either without any
|
||||
arguments or with the same arguments as the attribute that's
|
||||
being added. Return without adding another copy. */
|
||||
*no_add_attrs = true;
|
||||
return NULL_TREE;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL_TREE;
|
||||
|
|
|
@ -793,6 +793,16 @@ Wmisleading-indentation
|
|||
C C++ Common Var(warn_misleading_indentation) Warning LangEnabledBy(C C++,Wall)
|
||||
Warn when the indentation of the code does not reflect the block structure.
|
||||
|
||||
Wmismatched-dealloc
|
||||
C ObjC C++ ObjC++ Var(warn_mismatched_alloc) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
|
||||
Warn for deallocation calls with arguments returned from mismatched allocation
|
||||
functions.
|
||||
|
||||
Wmismatched-new-delete
|
||||
C++ ObjC++ Var(warn_mismatched_new_delete) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
|
||||
Warn for mismatches between calls to operator new or delete and the corrsponding
|
||||
call to the allocation or deallocation function.
|
||||
|
||||
Wmismatched-tags
|
||||
C++ ObjC++ Var(warn_mismatched_tags) Warning
|
||||
Warn when a class is redeclared or referenced using a mismatched class-key.
|
||||
|
|
|
@ -2623,6 +2623,10 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
|
|||
|
||||
/* Check attribute access arguments. */
|
||||
maybe_warn_rdwr_sizes (&rdwr_idx, fndecl, fntype, exp);
|
||||
|
||||
/* Check calls to operator new for mismatched forms and attempts
|
||||
to deallocate unallocated objects. */
|
||||
maybe_emit_free_warning (exp);
|
||||
}
|
||||
|
||||
/* Update ARGS_SIZE to contain the total size for the argument block.
|
||||
|
|
|
@ -3233,20 +3233,63 @@ this reason the attribute is not allowed on types to annotate indirect
|
|||
calls.
|
||||
|
||||
@item malloc
|
||||
@item malloc (@var{deallocator})
|
||||
@item malloc (@var{deallocator}, @var{ptr-index})
|
||||
@cindex @code{malloc} function attribute
|
||||
@cindex functions that behave like malloc
|
||||
This tells the compiler that a function is @code{malloc}-like, i.e.,
|
||||
that the pointer @var{P} returned by the function cannot alias any
|
||||
Attribute @code{malloc} indicates that a function is @code{malloc}-like,
|
||||
i.e., that the pointer @var{P} returned by the function cannot alias any
|
||||
other pointer valid when the function returns, and moreover no
|
||||
pointers to valid objects occur in any storage addressed by @var{P}.
|
||||
|
||||
Using this attribute can improve optimization. Compiler predicts
|
||||
that a function with the attribute returns non-null in most cases.
|
||||
Functions like
|
||||
@code{malloc} and @code{calloc} have this property because they return
|
||||
a pointer to uninitialized or zeroed-out storage. However, functions
|
||||
like @code{realloc} do not have this property, as they can return a
|
||||
pointer to storage containing pointers.
|
||||
Independently, the form of the attribute with one or two arguments
|
||||
associates @code{deallocator} as a suitable deallocation function for
|
||||
pointers returned from the @code{malloc}-like function. @var{ptr-index}
|
||||
denotes the positional argument to which when the pointer is passed in
|
||||
calls to @code{deallocator} has the effect of deallocating it.
|
||||
|
||||
Using the attribute with no arguments is designed to improve optimization.
|
||||
The compiler predicts that a function with the attribute returns non-null
|
||||
in most cases. Functions like @code{malloc} and @code{calloc} have this
|
||||
property because they return a pointer to uninitialized or zeroed-out
|
||||
storage. However, functions like @code{realloc} do not have this property,
|
||||
as they may return pointers to storage containing pointers to existing
|
||||
objects.
|
||||
|
||||
Associating a function with a @var{deallocator} helps detect calls to
|
||||
mismatched allocation and deallocation functions and diagnose them
|
||||
under the control of options such as @option{-Wmismatched-dealloc}.
|
||||
To indicate that an allocation function both satisifies the nonaliasing
|
||||
property and has a deallocator associated with it, both the plain form
|
||||
of the attribute and the one with the @var{deallocator} argument must
|
||||
be used.
|
||||
|
||||
For example, besides stating that the functions return pointers that do
|
||||
not alias any others, the following declarations make the @code{fclose}
|
||||
and @code{frepen} functions suitable deallocators for pointers returned
|
||||
from all the functions that return them, and the @code{pclose} function
|
||||
as the only other suitable deallocator besides @code{freopen} for pointers
|
||||
returned from @code{popen}. The deallocator functions must declared
|
||||
before they can be referenced in the attribute.
|
||||
|
||||
@smallexample
|
||||
int fclose (FILE*);
|
||||
FILE* freopen (const char*, const char*, FILE*);
|
||||
int pclose (FILE*);
|
||||
|
||||
__attribute__ ((malloc, malloc (fclose), malloc (freopen, 3)))
|
||||
FILE* fdopen (int);
|
||||
__attribute__ ((malloc, malloc (fclose), malloc (freopen, 3)))
|
||||
FILE* fopen (const char*, const char*);
|
||||
__attribute__ ((malloc, malloc (fclose), malloc (freopen, 3)))
|
||||
FILE* fmemopen(void *, size_t, const char *);
|
||||
__attribute__ ((malloc, malloc (fclose), malloc (freopen, 3)))
|
||||
FILE* freopen (const char*, const char*, FILE*);
|
||||
__attribute__ ((malloc, malloc (pclose), malloc (freopen, 3)))
|
||||
FILE* popen (const char*, const char*);
|
||||
__attribute__ ((malloc, malloc (fclose), malloc (freopen, 3)))
|
||||
FILE* tmpfile (void);
|
||||
@end smallexample
|
||||
|
||||
@item no_icf
|
||||
@cindex @code{no_icf} function attribute
|
||||
|
|
|
@ -242,7 +242,8 @@ in the following sections.
|
|||
-Wno-deprecated-enum-enum-conversion -Wno-deprecated-enum-float-conversion @gol
|
||||
-Weffc++ -Wno-exceptions -Wextra-semi -Wno-inaccessible-base @gol
|
||||
-Wno-inherited-variadic-ctor -Wno-init-list-lifetime @gol
|
||||
-Wno-invalid-offsetof -Wno-literal-suffix -Wmismatched-tags @gol
|
||||
-Wno-invalid-offsetof -Wno-literal-suffix @gol
|
||||
-Wno-mismatched-new-delete -Wmismatched-tags @gol
|
||||
-Wmultiple-inheritance -Wnamespaces -Wnarrowing @gol
|
||||
-Wnoexcept -Wnoexcept-type -Wnon-virtual-dtor @gol
|
||||
-Wpessimizing-move -Wno-placement-new -Wplacement-new=@var{n} @gol
|
||||
|
@ -3859,6 +3860,40 @@ The warning is inactive inside a system header file, such as the STL, so
|
|||
one can still use the STL. One may also instantiate or specialize
|
||||
templates.
|
||||
|
||||
@item -Wno-mismatched-new-delete @r{(C++ and Objective-C++ only)}
|
||||
@opindex Wmismatched-new-delete
|
||||
@opindex Wno-mismatched-new-delete
|
||||
Warn for mismatches between calls to @code{operator new} or @code{operator
|
||||
delete} and the corresponding call to the allocation or deallocation function.
|
||||
This includes invocations of C++ @code{operator delete} with pointers
|
||||
returned from either mismatched forms of @code{operator new}, or from other
|
||||
functions that allocate objects for which the @code{operator delete} isn't
|
||||
a suitable deallocator, as well as calls to other deallocation functions
|
||||
with pointers returned from @code{operator new} for which the deallocation
|
||||
function isn't suitable.
|
||||
|
||||
For example, the @code{delete} expression in the function below is diagnosed
|
||||
because it doesn't match the array form of the @code{new} expression
|
||||
the pointer argument was returned from. Similarly, the call to @code{free}
|
||||
is also diagnosed.
|
||||
|
||||
@smallexample
|
||||
void f ()
|
||||
@{
|
||||
int *a = new int[n];
|
||||
delete a; // warning: mismatch in array forms of expressions
|
||||
|
||||
char *p = new char[n];
|
||||
free (p); // warning: mismatch between new and free
|
||||
@}
|
||||
@end smallexample
|
||||
|
||||
The related option @option{-Wmismatched-dealloc} diagnoses mismatches
|
||||
involving allocation and deallocation functions other than @code{operator
|
||||
new} and @code{operator delete}.
|
||||
|
||||
@option{-Wmismatched-new-delete} is enabled by default.
|
||||
|
||||
@item -Wmismatched-tags @r{(C++ and Objective-C++ only)}
|
||||
@opindex Wmismatched-tags
|
||||
@opindex Wno-mismatched-tags
|
||||
|
@ -6287,6 +6322,41 @@ Ignoring the warning can result in poorly optimized code.
|
|||
disable the warning, but this is not recommended and should be done only
|
||||
when non-existent profile data is justified.
|
||||
|
||||
@item -Wno-mismatched-dealloc
|
||||
@opindex Wmismatched-dealloc
|
||||
@opindex Wno-mismatched-dealloc
|
||||
|
||||
Warn for calls to deallocation functions with pointer arguments returned
|
||||
from from allocations functions for which the former isn't a suitable
|
||||
deallocator. A pair of functions can be associated as matching allocators
|
||||
and deallocators by use of attribute @code{malloc}. Unless disabled by
|
||||
the @option{-fno-builtin} option the standard functions @code{calloc},
|
||||
@code{malloc}, @code{realloc}, and @code{free}, as well as the corresponding
|
||||
forms of C++ @code{operator new} and @code{operator delete} are implicitly
|
||||
associated as matching allocators and deallocators. In the following
|
||||
example @code{mydealloc} is the deallocator for pointers returned from
|
||||
@code{myalloc}.
|
||||
|
||||
@smallexample
|
||||
void mydealloc (void*);
|
||||
|
||||
__attribute__ ((malloc (mydealloc, 1))) void*
|
||||
myalloc (size_t);
|
||||
|
||||
void f (void)
|
||||
@{
|
||||
void *p = myalloc (32);
|
||||
// @dots{}use p@dots{}
|
||||
free (p); // warning: not a matching deallocator for myalloc
|
||||
mydealloc (p); // ok
|
||||
@}
|
||||
@end smallexample
|
||||
|
||||
In C++, the related option @option{-Wmismatched-new-delete} diagnoses
|
||||
mismatches involving either @code{operator new} or @code{operator delete}.
|
||||
|
||||
Option @option{-Wmismatched-dealloc} is enabled by default.
|
||||
|
||||
@item -Wmultistatement-macros
|
||||
@opindex Wmultistatement-macros
|
||||
@opindex Wno-multistatement-macros
|
||||
|
@ -7778,8 +7848,23 @@ to @option{-Wframe-larger-than=}@samp{SIZE_MAX} or larger.
|
|||
@item -Wno-free-nonheap-object
|
||||
@opindex Wno-free-nonheap-object
|
||||
@opindex Wfree-nonheap-object
|
||||
Do not warn when attempting to free an object that was not allocated
|
||||
on the heap.
|
||||
Warn when attempting to deallocate an object that was either not allocated
|
||||
on the heap, or by using a pointer that was not returned from a prior call
|
||||
to the corresponding allocation function. For example, because the call
|
||||
to @code{stpcpy} returns a pointer to the terminating nul character and
|
||||
not to the begginning of the object, the call to @code{free} below is
|
||||
diagnosed.
|
||||
|
||||
@smallexample
|
||||
void f (char *p)
|
||||
@{
|
||||
p = stpcpy (p, "abc");
|
||||
// ...
|
||||
free (p); // warning
|
||||
@}
|
||||
@end smallexample
|
||||
|
||||
@option{-Wfree-nonheap-object} is enabled by default.
|
||||
|
||||
@item -Wstack-usage=@var{byte-size}
|
||||
@opindex Wstack-usage
|
||||
|
|
|
@ -829,7 +829,7 @@ NOINLINE static int LargeFunction(bool do_bad_access) {
|
|||
x[18]++;
|
||||
x[19]++;
|
||||
|
||||
delete x;
|
||||
delete[] x;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
274
gcc/testsuite/g++.dg/warn/Wfree-nonheap-object-2.C
Normal file
274
gcc/testsuite/g++.dg/warn/Wfree-nonheap-object-2.C
Normal file
|
@ -0,0 +1,274 @@
|
|||
/* PR ????? - No warning on attempts to access free object
|
||||
Verify that freeing unallocated objects referenced either directly
|
||||
or through pointers is diagnosed.
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall -Wfree-nonheap-object" } */
|
||||
|
||||
typedef __INTPTR_TYPE__ intptr_t;
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
void free (void*);
|
||||
extern void* malloc (size_t);
|
||||
extern void* realloc (void *p, size_t);
|
||||
}
|
||||
|
||||
void sink (void*, ...);
|
||||
#define sink(...) sink (0, __VA_ARGS__)
|
||||
|
||||
extern char ecarr[];
|
||||
extern void* eparr[];
|
||||
|
||||
extern char *eptr;
|
||||
|
||||
void* source (void);
|
||||
|
||||
void nowarn_free (void *p, void **pp, size_t n, intptr_t iptr)
|
||||
{
|
||||
free (p);
|
||||
|
||||
p = 0;
|
||||
free (p);
|
||||
|
||||
p = malloc (n);
|
||||
sink (p);
|
||||
free (p);
|
||||
|
||||
p = malloc (n);
|
||||
sink (p);
|
||||
|
||||
p = realloc (p, n * 2);
|
||||
sink (p);
|
||||
free (p);
|
||||
|
||||
free ((void*)iptr);
|
||||
|
||||
p = source ();
|
||||
free (p);
|
||||
|
||||
p = source ();
|
||||
p = (char*)p - 1;
|
||||
free (p);
|
||||
|
||||
free (*pp);
|
||||
}
|
||||
|
||||
void warn_free_extern_arr (void)
|
||||
{
|
||||
free (ecarr); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
void warn_free_extern_arr_offset (int i)
|
||||
{
|
||||
char *p = ecarr + i;
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
|
||||
void warn_free_cstint (void)
|
||||
{
|
||||
void *p = (void*)1;
|
||||
sink (p);
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
|
||||
void warn_free_func (void)
|
||||
{
|
||||
void *p = (void*)warn_free_func;
|
||||
sink (p);
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
|
||||
void warn_free_string (int i)
|
||||
{
|
||||
{
|
||||
char *p = (char*)"123";
|
||||
sink (p);
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
{
|
||||
char *p = (char*)"234" + 1;
|
||||
sink (p);
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
{
|
||||
char *p = (char*)"345" + i;
|
||||
sink (p);
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
if (i >= 0)
|
||||
{
|
||||
char *p = (char*)"456" + i;
|
||||
sink (p);
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
}
|
||||
|
||||
void warn_free_local_arr (int i)
|
||||
{
|
||||
{
|
||||
char a[4];
|
||||
sink (a);
|
||||
free (a); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
{
|
||||
char b[5];
|
||||
sink (b);
|
||||
|
||||
char *p = b + 1;
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
{
|
||||
char c[6];
|
||||
sink (c);
|
||||
|
||||
char *p = c + i;
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void warn_free_vla (int n, int i)
|
||||
{
|
||||
{
|
||||
int vla[n], *p = vla;
|
||||
sink (p);
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
{
|
||||
int vla[n + 1], *p = vla + 1;
|
||||
sink (p);
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
{
|
||||
int vla[n + 2], *p = vla + i;
|
||||
sink (p);
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void nowarn_free_extern_ptrarr (void)
|
||||
{
|
||||
free (*eparr);
|
||||
}
|
||||
|
||||
void nowarn_free_extern_ptrarr_offset (int i)
|
||||
{
|
||||
void *p = eparr[i];
|
||||
free (p);
|
||||
}
|
||||
|
||||
|
||||
void warn_free_extern_ptrarr (void)
|
||||
{
|
||||
free (eparr); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
void warn_free_extern_ptrarr_offset (int i)
|
||||
{
|
||||
void *p = &eparr[i];
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
|
||||
void nowarn_free_local_ptrarr (int i)
|
||||
{
|
||||
void* a[4];
|
||||
sink (a);
|
||||
free (a[0]);
|
||||
free (a[1]);
|
||||
free (a[i]);
|
||||
}
|
||||
|
||||
|
||||
void nowarn_free_extern_ptr (void)
|
||||
{
|
||||
free (eptr);
|
||||
}
|
||||
|
||||
void nowarn_free_extern_ptr_offset (int i)
|
||||
{
|
||||
char *p = eptr + i;
|
||||
free (p);
|
||||
}
|
||||
|
||||
void warn_free_extern_ptr_pos_offset (int i)
|
||||
{
|
||||
if (i <= 0)
|
||||
i = 1;
|
||||
|
||||
char *q = eptr + i;
|
||||
free (q); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
|
||||
void nowarn_free_parm_offset (char *p, int i)
|
||||
{
|
||||
char *q = p + i;
|
||||
free (q);
|
||||
}
|
||||
|
||||
void nowarn_free_parm_neg_offset (char *p, int i)
|
||||
{
|
||||
if (i >= 0)
|
||||
i = -1;
|
||||
|
||||
char *q = p + i;
|
||||
free (q);
|
||||
}
|
||||
|
||||
void warn_free_parm_pos_offset (char *p, int i)
|
||||
{
|
||||
if (i <= 0)
|
||||
i = 1;
|
||||
|
||||
char *q = p + i;
|
||||
free (q); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
|
||||
struct Members
|
||||
{
|
||||
char a[4], *p, *q;
|
||||
};
|
||||
|
||||
extern struct Members em;
|
||||
|
||||
void nowarn_free_member_ptr (struct Members *pm, int i)
|
||||
{
|
||||
char *p = em.p;
|
||||
free (p);
|
||||
p = em.q + i;
|
||||
free (p);
|
||||
|
||||
free (pm->q);
|
||||
p = pm->p;
|
||||
free (pm);
|
||||
free (p);
|
||||
}
|
||||
|
||||
void nowarn_free_struct_cast (intptr_t *p)
|
||||
{
|
||||
struct Members *q = (struct Members*)*p;
|
||||
if (q->p == 0)
|
||||
free (q); // { dg-bogus "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
|
||||
void warn_free_member_array (void)
|
||||
{
|
||||
char *p = em.a;
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
void warn_free_member_array_off (int i)
|
||||
{
|
||||
char *p = em.a + i;
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
124
gcc/testsuite/g++.dg/warn/Wfree-nonheap-object.C
Normal file
124
gcc/testsuite/g++.dg/warn/Wfree-nonheap-object.C
Normal file
|
@ -0,0 +1,124 @@
|
|||
/* PR ????? - No warning on attempts to access free object
|
||||
Verify that attempts to deallocate objects by pointers with nonzero
|
||||
offsets is diagnosed.
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall -Wfree-nonheap-object" } */
|
||||
|
||||
typedef __INTPTR_TYPE__ intptr_t;
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
void sink (void*, ...);
|
||||
|
||||
extern char ecarr[];
|
||||
extern void* eparr[];
|
||||
|
||||
extern char *eptr;
|
||||
|
||||
char* source (void);
|
||||
|
||||
void nowarn_op_delete (void *p, void ***ppp, size_t n, intptr_t iptr)
|
||||
{
|
||||
operator delete (p);
|
||||
|
||||
p = 0;
|
||||
operator delete (p);
|
||||
|
||||
p = operator new (n);
|
||||
sink (p);
|
||||
operator delete (p);
|
||||
|
||||
p = operator new (n);
|
||||
sink (p);
|
||||
|
||||
operator delete ((void*)iptr);
|
||||
|
||||
p = source ();
|
||||
operator delete (p);
|
||||
|
||||
p = source ();
|
||||
p = (char*)p - 1;
|
||||
operator delete (p);
|
||||
|
||||
operator delete (**ppp);
|
||||
operator delete (*ppp);
|
||||
operator delete (ppp);
|
||||
}
|
||||
|
||||
void warn_op_delete_cstaddr (void *p)
|
||||
{
|
||||
operator delete (p);
|
||||
p = (void*)~0;
|
||||
operator delete (p); // { dg-warning "called on a pointer to an unallocated object" } */
|
||||
}
|
||||
|
||||
void warn_op_delete_funcaddr ()
|
||||
{
|
||||
void *p = (void*)&warn_op_delete_funcaddr;
|
||||
operator delete (p); // { dg-warning "called on unallocated object 'void warn_op_delete_funcaddr()" } */
|
||||
}
|
||||
|
||||
void warn_op_delete_string (void *p)
|
||||
{
|
||||
operator delete (p);
|
||||
p = (void*)"";
|
||||
operator delete (p); // { dg-warning "called on a pointer to an unallocated object" } */
|
||||
}
|
||||
|
||||
void warn_op_delete_ptr_to_self (void *p)
|
||||
{
|
||||
operator delete (p);
|
||||
p = &p;
|
||||
operator delete (p); // { dg-warning "called on unallocated object 'p'" } */
|
||||
}
|
||||
|
||||
void nowarn_op_new_delete (size_t n)
|
||||
{
|
||||
void *p = operator new (n);
|
||||
sink (p);
|
||||
operator delete (p);
|
||||
}
|
||||
|
||||
void nowarn_op_new_delete_ptr_plus (size_t n)
|
||||
{
|
||||
void *p0_1 = operator new (n);
|
||||
void *p1 = (char*)p0_1 + 1;
|
||||
sink (p0_1, p1);
|
||||
void *p0_2 = (char*)p1 - 1;
|
||||
sink (p0_1, p1, p0_2);
|
||||
operator delete (p0_2);
|
||||
}
|
||||
|
||||
void warn_op_new_delete_cstoff (size_t n)
|
||||
{
|
||||
void *p = operator new (n);
|
||||
void *q = (char*)p + 1;
|
||||
sink (p, q);
|
||||
operator delete (q); // { dg-warning "'void operator delete\\\(void\\\*\\\)' called on pointer '\[^'\]+' with nonzero offset 1" }
|
||||
}
|
||||
|
||||
void warn_op_new_delete_ptr_plus (size_t n)
|
||||
{
|
||||
char *p = (char*)operator new (n);
|
||||
sink (++p);
|
||||
operator delete (p); // { dg-warning "called on pointer '\[^']+' with nonzero offset 1" }
|
||||
}
|
||||
|
||||
void warn_op_delete_funcret_plus (size_t n)
|
||||
{
|
||||
char *p = source ();
|
||||
sink (++p);
|
||||
operator delete (p); // { dg-warning "called on pointer '\[^']+' with nonzero offset 1" }
|
||||
}
|
||||
|
||||
void warn_op_delete_eptr_plus (int i)
|
||||
{
|
||||
extern char *ecp;
|
||||
|
||||
if (i < 1)
|
||||
i = 1;
|
||||
|
||||
char *p = ecp + i;
|
||||
sink (p);
|
||||
|
||||
operator delete (p); // { dg-warning "called on pointer '\[^']+' with nonzero offset \\\[1, \\d+]" }
|
||||
}
|
0
gcc/testsuite/g++.dg/warn/Wfree-nonheap-object.s
Normal file
0
gcc/testsuite/g++.dg/warn/Wfree-nonheap-object.s
Normal file
185
gcc/testsuite/g++.dg/warn/Wmismatched-dealloc-2.C
Normal file
185
gcc/testsuite/g++.dg/warn/Wmismatched-dealloc-2.C
Normal file
|
@ -0,0 +1,185 @@
|
|||
/* PR middle-end/94527 - Add an attribute that marks a function as freeing
|
||||
an object
|
||||
The detection doesn't require optimization.
|
||||
{ dg-do compile }
|
||||
{ dg-options "-Wall" } */
|
||||
|
||||
#define A(...) __attribute__ ((malloc (__VA_ARGS__)))
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
extern "C" {
|
||||
void free (void *);
|
||||
void* realloc (void *, size_t);
|
||||
}
|
||||
|
||||
void sink (void *);
|
||||
|
||||
void mydealloc (int, void*);
|
||||
void* A (mydealloc, 2) myalloc (void*);
|
||||
|
||||
|
||||
void my_delete (const char*, void*);
|
||||
void my_array_delete (const char*, void*);
|
||||
|
||||
typedef void OpDelete1 (void*);
|
||||
typedef void OpDelete2 (void*, size_t);
|
||||
|
||||
A ((OpDelete1*)operator delete, 1)
|
||||
#if __cplusplus >= 201402L
|
||||
A ((OpDelete2*)operator delete, 1)
|
||||
#endif
|
||||
A (my_delete, 2)
|
||||
int* my_new (size_t);
|
||||
|
||||
A ((OpDelete1*)operator delete[], 1)
|
||||
#if __cplusplus >= 201402L
|
||||
A ((OpDelete2*)operator delete[], 1)
|
||||
#endif
|
||||
A (my_array_delete, 2)
|
||||
int* my_array_new (size_t);
|
||||
|
||||
|
||||
void test_my_new ()
|
||||
{
|
||||
{
|
||||
void *p = my_new (1);
|
||||
operator delete (p);
|
||||
}
|
||||
{
|
||||
void *p = my_new (1);
|
||||
sink (p);
|
||||
operator delete (p);
|
||||
}
|
||||
{
|
||||
int *p = my_new (1);
|
||||
sink (p);
|
||||
delete p;
|
||||
}
|
||||
|
||||
{
|
||||
void *p = my_new (1);
|
||||
// { dg-message "returned from a call to 'int\\\* my_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
|
||||
operator delete[] (p);
|
||||
// { dg-warning "'void operator delete \\\[]\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function \\\[-Wmismatched-new-delete" "" { target *-*-* } .-1 }
|
||||
}
|
||||
{
|
||||
void *p = my_new (1);
|
||||
// { dg-message "returned from a call to 'int\\\* my_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
|
||||
sink (p);
|
||||
operator delete[] (p);
|
||||
// { dg-warning "'void operator delete \\\[]\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function \\\[-Wmismatched-new-delete" "" { target *-*-* } .-1 }
|
||||
}
|
||||
{
|
||||
int *p = my_new (1);
|
||||
sink (p);
|
||||
delete[] p;
|
||||
// { dg-warning "'void operator delete \\\[]\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function \\\[-Wmismatched-new-delete" "" { target *-*-* } .-1 }
|
||||
}
|
||||
|
||||
{
|
||||
void *p = my_new (1);
|
||||
my_delete ("1", p);
|
||||
}
|
||||
{
|
||||
void *p = my_new (1);
|
||||
sink (p);
|
||||
my_delete ("2", p);
|
||||
}
|
||||
|
||||
{
|
||||
void *p = my_new (1);
|
||||
// { dg-message "returned from a call to 'int\\\* my_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
|
||||
sink (p);
|
||||
my_array_delete ("3", p);
|
||||
// { dg-warning "'void my_array_delete\\\(const char\\\*, void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
|
||||
}
|
||||
|
||||
{
|
||||
void *p = my_new (1);
|
||||
// { dg-message "returned from a call to 'int\\\* my_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
|
||||
sink (p);
|
||||
free (p);
|
||||
// { dg-warning "'void free\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
|
||||
}
|
||||
|
||||
{
|
||||
void *p = my_new (1);
|
||||
// { dg-message "returned from a call to 'int\\\* my_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
|
||||
sink (p);
|
||||
p = realloc (p, 123);
|
||||
// { dg-warning "'void\\\* realloc\\\(void\\\*, size_t\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void test_my_array_new ()
|
||||
{
|
||||
{
|
||||
void *p = my_array_new (1);
|
||||
operator delete[] (p);
|
||||
}
|
||||
{
|
||||
void *p = my_array_new (1);
|
||||
sink (p);
|
||||
operator delete[] (p);
|
||||
}
|
||||
{
|
||||
int *p = my_array_new (1);
|
||||
sink (p);
|
||||
delete[] p;
|
||||
}
|
||||
|
||||
{
|
||||
void *p = my_array_new (1);
|
||||
// { dg-message "returned from a call to 'int\\\* my_array_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
|
||||
operator delete (p);
|
||||
// { dg-warning "'void operator delete\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function \\\[-Wmismatched-new-delete" "" { target *-*-* } .-1 }
|
||||
}
|
||||
{
|
||||
void *p = my_array_new (1);
|
||||
// { dg-message "returned from a call to 'int\\\* my_array_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
|
||||
sink (p);
|
||||
operator delete (p);
|
||||
// { dg-warning "'void operator delete\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function \\\[-Wmismatched-new-delete" "" { target *-*-* } .-1 }
|
||||
}
|
||||
{
|
||||
int *p = my_array_new (1);
|
||||
sink (p);
|
||||
delete p;
|
||||
// { dg-warning "'void operator delete\\\(void\\\*\[^\)\]*\\\)' called on pointer returned from a mismatched allocation function \\\[-Wmismatched-new-delete" "" { target *-*-* } .-1 }
|
||||
}
|
||||
|
||||
{
|
||||
void *p = my_array_new (1);
|
||||
my_array_delete ("1", p);
|
||||
}
|
||||
{
|
||||
void *p = my_array_new (1);
|
||||
sink (p);
|
||||
my_array_delete ("2", p);
|
||||
}
|
||||
{
|
||||
void *p = my_array_new (1);
|
||||
// { dg-message "returned from a call to 'int\\\* my_array_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
|
||||
sink (p);
|
||||
my_delete ("3", p);
|
||||
// { dg-warning "'void my_delete\\\(const char\\\*, void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
|
||||
}
|
||||
|
||||
{
|
||||
void *p = my_array_new (1);
|
||||
// { dg-message "returned from a call to 'int\\\* my_array_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
|
||||
sink (p);
|
||||
free (p);
|
||||
// { dg-warning "'void free\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
|
||||
}
|
||||
|
||||
{
|
||||
void *p = my_array_new (1);
|
||||
// { dg-message "returned from a call to 'int\\\* my_array_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
|
||||
sink (p);
|
||||
p = realloc (p, 123);
|
||||
// { dg-warning "'void\\\* realloc\\\(void\\\*, size_t\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
|
||||
}
|
||||
}
|
27
gcc/testsuite/g++.dg/warn/Wmismatched-dealloc.C
Normal file
27
gcc/testsuite/g++.dg/warn/Wmismatched-dealloc.C
Normal file
|
@ -0,0 +1,27 @@
|
|||
/* PR middle-end/94527 - Add an attribute that marks a function as freeing
|
||||
an object
|
||||
{ dg-do compile { target c++11 } }
|
||||
{ dg-options "-Wall" } */
|
||||
|
||||
#define A(...) __attribute__ ((malloc (__VA_ARGS__)))
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
void mydealloc (int, void*);
|
||||
void* A (mydealloc, 2) myalloc (void*);
|
||||
|
||||
|
||||
void* A (operator delete, 1)
|
||||
bad_new (size_t); // { dg-error "attribute argument 1 is ambiguous" }
|
||||
void* A (operator delete[], 1)
|
||||
bad_array_new (size_t); // { dg-error "attribute argument 1 is ambiguous" }
|
||||
|
||||
void my_delete (const char*, void*);
|
||||
void my_array_delete (const char*, void*);
|
||||
|
||||
typedef void OpDelete (void*);
|
||||
|
||||
int* A ((OpDelete*)operator delete, 1) A (my_delete, 2)
|
||||
my_new (size_t);
|
||||
int* A ((OpDelete*)operator delete[], 1) A (my_array_delete, 2)
|
||||
my_array_new (size_t);
|
212
gcc/testsuite/g++.dg/warn/Wmismatched-new-delete.C
Normal file
212
gcc/testsuite/g++.dg/warn/Wmismatched-new-delete.C
Normal file
|
@ -0,0 +1,212 @@
|
|||
/* PR c++/90629 - Support for -Wmismatched-new-delete
|
||||
The detection doesn't require optimization.
|
||||
{ dg-do compile }
|
||||
{ dg-options "-Wall" } */
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
extern "C" {
|
||||
void free (void *);
|
||||
void* malloc (size_t);
|
||||
void* realloc (void *, size_t);
|
||||
char* strdup (const char *);
|
||||
char* strndup (const char *, size_t);
|
||||
}
|
||||
|
||||
void sink (void *);
|
||||
|
||||
void nowarn_op_new_delete (int n)
|
||||
{
|
||||
void *p = operator new (n);
|
||||
sink (p);
|
||||
operator delete (p);
|
||||
}
|
||||
|
||||
void nowarn_new_delete (int n)
|
||||
{
|
||||
{
|
||||
char *p = new char;
|
||||
sink (p);
|
||||
delete p;
|
||||
}
|
||||
|
||||
{
|
||||
char *p = new char[n];
|
||||
sink (p);
|
||||
delete[] p;
|
||||
}
|
||||
}
|
||||
|
||||
/* Verify a warning for calls to free() with a pointer returned from
|
||||
a call to operator new() or the new expressopm. */
|
||||
|
||||
void warn_new_free (int n)
|
||||
{
|
||||
{
|
||||
void *p = operator new (n);
|
||||
// { dg-message "returned from a call to 'void\\\* operator new\\\(" "note" { target *-*-* } .-1 }
|
||||
sink (p);
|
||||
free (p);
|
||||
// { dg-warning "'void free\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
|
||||
}
|
||||
{
|
||||
char *p = new char[n];
|
||||
// { dg-message "returned from a call to 'void\\\* operator new \\\[" "note" { target *-*-* } .-1 }
|
||||
sink (p);
|
||||
free (p);
|
||||
// { dg-warning "'void free\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Verify a warning for calls to realloc() with a pointer returned from
|
||||
a call to operator new() or the new expressopm. */
|
||||
|
||||
void warn_new_realloc (int n)
|
||||
{
|
||||
{
|
||||
void *p = operator new (n);
|
||||
// { dg-message "returned from a call to 'void\\\* operator new\\\(" "note" { target *-*-* } .-1 }
|
||||
sink (p);
|
||||
p = realloc (p, n * 2);
|
||||
// { dg-warning "'void\\\* realloc\\\(\[^)\]+\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
|
||||
sink (p);
|
||||
}
|
||||
{
|
||||
void *p = new char[n];
|
||||
// { dg-message "returned from a call to 'void\\\* operator new \\\[" "note" { target *-*-* } .-1 }
|
||||
sink (p);
|
||||
p = realloc (p, n * 2);
|
||||
// { dg-warning "'void\\\* realloc\\\(\[^)\]+\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
|
||||
sink (p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Verify a warning for a call to operator_delete() with a pointer returned
|
||||
from a call to malloc(). */
|
||||
|
||||
void warn_malloc_op_delete (int n)
|
||||
{
|
||||
char *p = (char *)malloc (n);
|
||||
// { dg-message "returned from a call to 'void\\\* malloc\\\(" "note" { target *-*-* } .-1 }
|
||||
sink (p);
|
||||
operator delete (p);
|
||||
// { dg-warning "'void operator delete\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
|
||||
}
|
||||
|
||||
|
||||
/* Verify a warning for an invocation of either form of the delete
|
||||
expression with a pointer returned from a call to malloc(). */
|
||||
|
||||
void warn_malloc_delete (int n)
|
||||
{
|
||||
{
|
||||
char *p = (char *)malloc (n);
|
||||
// { dg-message "returned from a call to 'void\\\* malloc\\\(" "note" { target *-*-* } .-1 }
|
||||
sink (p);
|
||||
/* C++98 calls operator delete (void*) but later versions call
|
||||
operator delete (void*, size_t). The difference doesn't matter
|
||||
here so verify just that some operator delete is called. */
|
||||
delete p;
|
||||
// { dg-warning "'void operator delete\\\(\[^)\]+\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
|
||||
}
|
||||
|
||||
{
|
||||
char *p = (char *)malloc (n);
|
||||
// { dg-message "returned from a call to 'void\\\* malloc\\\(" "note" { target *-*-* } .-1 }
|
||||
sink (p);
|
||||
delete[] p;
|
||||
// { dg-warning "'void operator delete \\\[]\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Verify a warning for an invocation of either form of the delete
|
||||
expression with a pointer returned from a call to realloc(). */
|
||||
|
||||
void warn_realloc_delete (void *p1, void *p2, int n)
|
||||
{
|
||||
{
|
||||
char *q = (char *)realloc (p1, n);
|
||||
// { dg-message "returned from a call to 'void\\\* realloc\\\(" "note" { target *-*-* } .-1 }
|
||||
sink (q);
|
||||
/* C++98 calls operator delete (void*) but later versions call
|
||||
operator delete (void*, size_t). The difference doesn't matter
|
||||
here so verify just that some operator delete is called. */
|
||||
delete q;
|
||||
// { dg-warning "'void operator delete\\\(\[^)\]+\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
|
||||
}
|
||||
|
||||
{
|
||||
char *q = (char *)realloc (p2, n);
|
||||
// { dg-message "returned from a call to 'void\\\* realloc\\\(" "note" { target *-*-* } .-1 }
|
||||
sink (q);
|
||||
delete[] q;
|
||||
// { dg-warning "'void operator delete \\\[]\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Verify a warning for an invocation of either form of the delete
|
||||
expression with a pointer returned from a call to strdup(). */
|
||||
|
||||
void warn_strdup_delete (const char *s1, const char *s2)
|
||||
{
|
||||
{
|
||||
char *q = strdup (s1);
|
||||
// { dg-message "returned from a call to 'char\\\* strdup\\\(" "note" { target *-*-* } .-1 }
|
||||
sink (q);
|
||||
/* C++98 calls operator delete (void*) but later versions call
|
||||
operator delete (void*, size_t). The difference doesn't matter
|
||||
here so verify just that some operator delete is called. */
|
||||
delete q;
|
||||
// { dg-warning "'void operator delete\\\(\[^)\]+\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
|
||||
}
|
||||
|
||||
{
|
||||
char *q = strdup (s2);
|
||||
// { dg-message "returned from a call to 'char\\\* strdup\\\(" "note" { target *-*-* } .-1 }
|
||||
sink (q);
|
||||
delete[] q;
|
||||
// { dg-warning "'void operator delete \\\[]\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Verify a warning for an invocation of either form of the delete
|
||||
expression with a pointer returned from a call to strndup(). */
|
||||
|
||||
void warn_strdup_delete (const char *s1, const char *s2, size_t n)
|
||||
{
|
||||
{
|
||||
char *q = strndup (s1, n);
|
||||
// { dg-message "returned from a call to 'char\\\* strndup\\\(" "note" { target *-*-* } .-1 }
|
||||
sink (q);
|
||||
/* C++98 calls operator delete (void*) but later versions call
|
||||
operator delete (void*, size_t). The difference doesn't matter
|
||||
here so verify just that some operator delete is called. */
|
||||
delete q;
|
||||
// { dg-warning "'void operator delete\\\(\[^)\]+\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
|
||||
}
|
||||
|
||||
{
|
||||
char *q = strndup (s2, n);
|
||||
// { dg-message "returned from a call to 'char\\\* strndup\\\(" "note" { target *-*-* } .-1 }
|
||||
sink (q);
|
||||
delete[] q;
|
||||
// { dg-warning "'void operator delete \\\[]\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct Base { virtual ~Base (); };
|
||||
struct Derived: Base { };
|
||||
|
||||
void warn_new_free_base_derived ()
|
||||
{
|
||||
Base *p = new Derived ();
|
||||
sink (p);
|
||||
free (p); // { dg-warning "\\\[-Wmismatched-new-delete" }
|
||||
}
|
|
@ -5,7 +5,7 @@ struct S { int a [1]; } s;
|
|||
|
||||
void foo (S *p)
|
||||
{
|
||||
delete a; // { dg-warning "deleting array" }
|
||||
delete s.a; // { dg-warning "deleting array" }
|
||||
delete p->a; // { dg-warning "deleting array" }
|
||||
delete a; // { dg-warning "deleting array|-Wfree-nonheap-object" }
|
||||
delete s.a; // { dg-warning "deleting array|-Wfree-nonheap-object" }
|
||||
delete p->a; // { dg-warning "deleting array|-Wfree-nonheap-object" }
|
||||
}
|
||||
|
|
|
@ -9,5 +9,7 @@ void bar(foo a) {
|
|||
delete[] a; // should be accepted
|
||||
char b[1];
|
||||
delete b; // { dg-warning "deleting array" } expecting pointer type
|
||||
// { dg-warning "-Wfree-nonheap-object" "" { target *-*-* } .-1 }
|
||||
delete[] b; // { dg-warning "deleting array" } expecting pointer type
|
||||
// { dg-warning "-Wfree-nonheap-object" "" { target *-*-* } .-1 }
|
||||
}
|
||||
|
|
279
gcc/testsuite/gcc.dg/Wfree-nonheap-object-2.c
Normal file
279
gcc/testsuite/gcc.dg/Wfree-nonheap-object-2.c
Normal file
|
@ -0,0 +1,279 @@
|
|||
/* PR ????? - No warning on attempts to access free object
|
||||
Verify that attempting to reallocate unallocated objects referenced
|
||||
either directly or through pointers is diagnosed.
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall -Wfree-nonheap-object" } */
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
extern void free (void*);
|
||||
extern void* alloca (size_t);
|
||||
extern void* realloc (void*, size_t);
|
||||
|
||||
void sink (void*, ...);
|
||||
|
||||
extern void* eparr[];
|
||||
extern char *eptr;
|
||||
|
||||
extern size_t n;
|
||||
|
||||
|
||||
void nowarn_realloc (void *p, size_t n)
|
||||
{
|
||||
char *q = realloc (p, n);
|
||||
sink (q);
|
||||
|
||||
q = realloc (0, n);
|
||||
sink (q);
|
||||
|
||||
q = realloc (q, n * 2);
|
||||
sink (q);
|
||||
}
|
||||
|
||||
/* Verify that calling realloc on a pointer to an unknown object minus
|
||||
some nonzero offset isn't diagnosed, but a pointer plus a positive
|
||||
offset is (a positive offset cannot point at the beginning). */
|
||||
|
||||
void test_realloc_offset (char *p1, char *p2, char *p3, size_t n, int i)
|
||||
{
|
||||
char *q;
|
||||
q = realloc (p1 - 1, n);
|
||||
sink (q);
|
||||
|
||||
q = realloc (p2 + 1, n); // { dg-warning "'realloc' called on pointer 'p2' with nonzero offset 1" }
|
||||
sink (q);
|
||||
|
||||
q = realloc (p3 + i, n);
|
||||
sink (q);
|
||||
}
|
||||
|
||||
void warn_realloc_extern_arr (void)
|
||||
{
|
||||
extern char ecarr[]; // { gg-message "declared here" }
|
||||
char *p = ecarr;
|
||||
char *q = realloc (p, n); // { dg-warning "'realloc' called on unallocated object 'ecarr'" }
|
||||
sink (q);
|
||||
}
|
||||
|
||||
void warn_realloc_extern_arr_offset (int i)
|
||||
{
|
||||
extern char ecarr[];
|
||||
char *p = ecarr + i;
|
||||
char *q = realloc (p, n); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
sink (q);
|
||||
}
|
||||
|
||||
|
||||
void warn_realloc_string (int i)
|
||||
{
|
||||
char *p, *q;
|
||||
{
|
||||
p = "123";
|
||||
sink (p);
|
||||
q = realloc (p, n); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
sink (q);
|
||||
}
|
||||
{
|
||||
p = "234" + 1;
|
||||
sink (p);
|
||||
q = realloc (p, n); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
sink (q);
|
||||
}
|
||||
{
|
||||
p = "123" + i;
|
||||
sink (p);
|
||||
q = realloc (p, n); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
sink (q);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void warn_realloc_alloca (int n, int i)
|
||||
{
|
||||
char *p, *q;
|
||||
{
|
||||
p = alloca (n);
|
||||
sink (p);
|
||||
q = realloc (p, n); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
sink (q);
|
||||
}
|
||||
{
|
||||
p = (char*)alloca (n + 1);
|
||||
sink (p);
|
||||
q = realloc (p, n); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
sink (q);
|
||||
}
|
||||
{
|
||||
p = (char*)alloca (n + 2) + i;
|
||||
sink (p);
|
||||
q = realloc (p, n); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
sink (q);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void warn_realloc_local_arr (int i)
|
||||
{
|
||||
char *q;
|
||||
{
|
||||
char a[4];
|
||||
sink (a);
|
||||
q = realloc (a, n); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
sink (q);
|
||||
}
|
||||
|
||||
{
|
||||
char b[5];
|
||||
sink (b);
|
||||
q = realloc (b + 1, n); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
sink (q);
|
||||
}
|
||||
|
||||
{
|
||||
char c[6];
|
||||
sink (c);
|
||||
q = realloc (&c[2], n); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
sink (q);
|
||||
}
|
||||
|
||||
{
|
||||
char d[7];
|
||||
sink (d);
|
||||
q = realloc (&d[i], n); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
sink (q);
|
||||
}
|
||||
}
|
||||
|
||||
void warn_realloc_vla (int n1, int n2, int i)
|
||||
{
|
||||
char *q;
|
||||
{
|
||||
char vla[n1];
|
||||
sink (vla);
|
||||
q = realloc (vla, n2); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
sink (q);
|
||||
}
|
||||
|
||||
{
|
||||
char vlb[n1 + 1];
|
||||
sink (vlb);
|
||||
q = realloc (vlb + 1, n2);// { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
sink (q);
|
||||
}
|
||||
|
||||
{
|
||||
char vlc[n1 + 2];
|
||||
sink (vlc);
|
||||
q = realloc (&vlc[2], n2);// { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
sink (q);
|
||||
}
|
||||
|
||||
{
|
||||
char vld[7];
|
||||
sink (vld);
|
||||
q = realloc (&vld[i], n2);// { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
sink (q);
|
||||
}
|
||||
}
|
||||
|
||||
void nowarn_realloc_extern_ptrarr (void)
|
||||
{
|
||||
char *q = realloc (*eparr, n);
|
||||
sink (q);
|
||||
}
|
||||
|
||||
void nowarn_realloc_extern_ptrarr_offset (int i)
|
||||
{
|
||||
char *p = eparr[i];
|
||||
char *q = realloc (p, n);
|
||||
sink (q);
|
||||
}
|
||||
|
||||
|
||||
void warn_realloc_extern_ptrarr (void)
|
||||
{
|
||||
char *q = realloc (eparr, n); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
sink (q);
|
||||
}
|
||||
|
||||
void warn_realloc_extern_ptrarr_offset (int i)
|
||||
{
|
||||
void *p = eparr + i;
|
||||
void *q = realloc (p, n); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
sink (q);
|
||||
}
|
||||
|
||||
|
||||
void nowarn_realloc_extern_ptr (void)
|
||||
{
|
||||
char *q = realloc (eptr, n);
|
||||
sink (q);
|
||||
}
|
||||
|
||||
void nowarn_realloc_extern_ptr_offset (int i)
|
||||
{
|
||||
char *p = eptr + i;
|
||||
char *q = realloc (p, n);
|
||||
sink (q);
|
||||
}
|
||||
|
||||
|
||||
void warn_realloc_extern_ptr_pos_offset (int i)
|
||||
{
|
||||
if (i <= 0)
|
||||
i = 1;
|
||||
|
||||
char *p = eptr + i;
|
||||
char *q = realloc (p, n); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
sink (q);
|
||||
}
|
||||
|
||||
|
||||
void nowarn_realloc_parm_offset (char *p, int i)
|
||||
{
|
||||
char *q = p + i;
|
||||
q = realloc (q, n);
|
||||
sink (q);
|
||||
}
|
||||
|
||||
void nowarn_realloc_parm_neg_offset (char *p, int i)
|
||||
{
|
||||
if (i >= 0)
|
||||
i = -1;
|
||||
|
||||
char *q = p + i;
|
||||
q = realloc (q, n);
|
||||
sink (q);
|
||||
}
|
||||
|
||||
void warn_realloc_parm_pos_offset (char *p, int i)
|
||||
{
|
||||
if (i <= 0)
|
||||
i = 1;
|
||||
|
||||
char *q = p + i;
|
||||
q = realloc (q, n); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
sink (q);
|
||||
}
|
||||
|
||||
void nowarn_realloc_deref_parm_pos_offset (void **p, int i)
|
||||
{
|
||||
if (i <= 0)
|
||||
i = 1;
|
||||
|
||||
// The offset is from p, not *p.
|
||||
void *q = *(p + i);
|
||||
q = realloc (q, n);
|
||||
sink (q);
|
||||
}
|
||||
|
||||
void warn_realloc_deref_parm_pos_offset (void **p, int i)
|
||||
{
|
||||
if (i <= 0)
|
||||
i = 1;
|
||||
|
||||
// Unlike in the function above the offset is from *p.
|
||||
void *q = *p + i;
|
||||
q = realloc (q, n); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
sink (q);
|
||||
}
|
57
gcc/testsuite/gcc.dg/Wfree-nonheap-object-3.c
Normal file
57
gcc/testsuite/gcc.dg/Wfree-nonheap-object-3.c
Normal file
|
@ -0,0 +1,57 @@
|
|||
/* PR ????? - No warning on attempts to access free object
|
||||
Verify that freeing unallocated objects referenced indirectly through
|
||||
pointers obtained from function calls is diagnosed.
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall -Wfree-nonheap-object" } */
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
extern void free (void*);
|
||||
extern char* memchr (const void*, int, size_t);
|
||||
extern char* strchr (const char*, int);
|
||||
|
||||
void sink (void*, ...);
|
||||
|
||||
extern char ecarr[];
|
||||
extern void* eparr[];
|
||||
|
||||
extern char *eptr;
|
||||
|
||||
|
||||
void warn_free_memchr_ecarr (int x, size_t n)
|
||||
{
|
||||
char *p = memchr (ecarr, x, n);
|
||||
sink (p);
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
void warn_free_memchr_ecarr_offset (int i, int j, int x, size_t n)
|
||||
{
|
||||
char *p = memchr (ecarr + i, x, n);
|
||||
char *q = p + j;
|
||||
sink (p, q);
|
||||
free (q); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
|
||||
void warn_free_memchr_local_arr (int x, size_t n)
|
||||
{
|
||||
char a[8];
|
||||
sink (a);
|
||||
|
||||
char *p = memchr (a, x, n);
|
||||
sink (p);
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
void warn_free_memchr_local_arr_offset (int i, int j, int x, size_t n)
|
||||
{
|
||||
char a[8];
|
||||
sink (a);
|
||||
|
||||
char *p = memchr (a + i, x, n);
|
||||
char *q = p + j;
|
||||
sink (p, q);
|
||||
free (q); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
273
gcc/testsuite/gcc.dg/Wfree-nonheap-object.c
Normal file
273
gcc/testsuite/gcc.dg/Wfree-nonheap-object.c
Normal file
|
@ -0,0 +1,273 @@
|
|||
/* PR ????? - No warning on attempts to access free object
|
||||
Verify that freeing unallocated objects referenced either directly
|
||||
or through pointers is diagnosed. In most cases this doesn't require
|
||||
optimization.
|
||||
{ dg-do compile }
|
||||
{ dg-options "-Wall -Wfree-nonheap-object" } */
|
||||
|
||||
typedef __INTPTR_TYPE__ intptr_t;
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
extern void free (void*);
|
||||
extern void* malloc (size_t);
|
||||
extern void* realloc (void *p, size_t);
|
||||
|
||||
void sink (void*, ...);
|
||||
|
||||
extern char ecarr[];
|
||||
extern void* eparr[];
|
||||
|
||||
extern char *eptr;
|
||||
|
||||
void* source (void);
|
||||
|
||||
void nowarn_free (void *p, void **pp, size_t n, intptr_t iptr)
|
||||
{
|
||||
free (p);
|
||||
|
||||
p = 0;
|
||||
free (p);
|
||||
|
||||
p = malloc (n);
|
||||
sink (p);
|
||||
free (p);
|
||||
|
||||
p = malloc (n);
|
||||
sink (p);
|
||||
|
||||
p = realloc (p, n * 2);
|
||||
sink (p);
|
||||
free (p);
|
||||
|
||||
free ((void*)iptr);
|
||||
|
||||
p = source ();
|
||||
free (p);
|
||||
|
||||
p = source ();
|
||||
p = (char*)p - 1;
|
||||
free (p);
|
||||
|
||||
free (*pp);
|
||||
}
|
||||
|
||||
void warn_free_extern_arr (void)
|
||||
{
|
||||
free (ecarr); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
void warn_free_extern_arr_offset (int i)
|
||||
{
|
||||
char *p = ecarr + i;
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
|
||||
void warn_free_cstint (void)
|
||||
{
|
||||
void *p = (void*)1;
|
||||
sink (p);
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
|
||||
void warn_free_func (void)
|
||||
{
|
||||
void *p = warn_free_func;
|
||||
sink (p);
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
|
||||
void warn_free_string (int i)
|
||||
{
|
||||
{
|
||||
char *p = "123";
|
||||
sink (p);
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
{
|
||||
char *p = "234" + 1;
|
||||
sink (p);
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
{
|
||||
char *p = "345" + i;
|
||||
sink (p);
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
if (i >= 0)
|
||||
{
|
||||
char *p = "456" + i;
|
||||
sink (p);
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
}
|
||||
|
||||
void warn_free_local_arr (int i)
|
||||
{
|
||||
{
|
||||
char a[4];
|
||||
sink (a);
|
||||
free (a); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
{
|
||||
char b[5];
|
||||
sink (b);
|
||||
|
||||
char *p = b + 1;
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
{
|
||||
char c[6];
|
||||
sink (c);
|
||||
|
||||
char *p = c + i;
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void warn_free_vla (int n, int i)
|
||||
{
|
||||
{
|
||||
int vla[n], *p = vla;
|
||||
sink (p);
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
{
|
||||
int vla[n + 1], *p = vla + 1;
|
||||
sink (p);
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
{
|
||||
int vla[n + 2], *p = vla + i;
|
||||
sink (p);
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void nowarn_free_extern_ptrarr (void)
|
||||
{
|
||||
free (*eparr);
|
||||
}
|
||||
|
||||
void nowarn_free_extern_ptrarr_offset (int i)
|
||||
{
|
||||
char *p = eparr[i];
|
||||
free (p);
|
||||
}
|
||||
|
||||
|
||||
void warn_free_extern_ptrarr (void)
|
||||
{
|
||||
free (eparr); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
void warn_free_extern_ptrarr_offset (int i)
|
||||
{
|
||||
void *p = &eparr[i];
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
|
||||
void nowarn_free_local_ptrarr (int i)
|
||||
{
|
||||
void* a[4];
|
||||
sink (a);
|
||||
free (a[0]);
|
||||
free (a[1]);
|
||||
free (a[i]);
|
||||
}
|
||||
|
||||
|
||||
void nowarn_free_extern_ptr (void)
|
||||
{
|
||||
free (eptr);
|
||||
}
|
||||
|
||||
void nowarn_free_extern_ptr_offset (int i)
|
||||
{
|
||||
char *p = eptr + i;
|
||||
free (p);
|
||||
}
|
||||
|
||||
void nowarn_free_parm_offset (char *p, int i)
|
||||
{
|
||||
char *q = p + i;
|
||||
free (q);
|
||||
}
|
||||
|
||||
void nowarn_free_parm_neg_offset (char *p, int i)
|
||||
{
|
||||
if (i >= 0)
|
||||
i = -1;
|
||||
|
||||
char *q = p + i;
|
||||
free (q);
|
||||
}
|
||||
|
||||
struct Members
|
||||
{
|
||||
char a[4], *p, *q;
|
||||
};
|
||||
|
||||
extern struct Members em;
|
||||
|
||||
void nowarn_free_member_ptr (struct Members *pm, int i)
|
||||
{
|
||||
char *p = em.p;
|
||||
free (p);
|
||||
p = em.q + i;
|
||||
free (p);
|
||||
|
||||
free (pm->q);
|
||||
p = pm->p;
|
||||
free (pm);
|
||||
free (p);
|
||||
}
|
||||
|
||||
void nowarn_free_struct_cast (intptr_t *p)
|
||||
{
|
||||
struct Members *q = (struct Members*)*p;
|
||||
if (q->p == 0)
|
||||
free (q); // { dg-bogus "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
|
||||
void warn_free_member_array (void)
|
||||
{
|
||||
char *p = em.a;
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
void warn_free_member_array_off (int i)
|
||||
{
|
||||
char *p = em.a + i;
|
||||
free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
|
||||
// Range information requires optimization.
|
||||
#pragma GCC optimize "1"
|
||||
|
||||
void warn_free_extern_ptr_pos_offset (int i)
|
||||
{
|
||||
if (i <= 0)
|
||||
i = 1;
|
||||
|
||||
char *q = eptr + i;
|
||||
free (q); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
void warn_free_parm_pos_offset (char *p, int i)
|
||||
{
|
||||
if (i <= 0)
|
||||
i = 1;
|
||||
|
||||
char *q = p + i;
|
||||
free (q); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
252
gcc/testsuite/gcc.dg/Wmismatched-dealloc.c
Normal file
252
gcc/testsuite/gcc.dg/Wmismatched-dealloc.c
Normal file
|
@ -0,0 +1,252 @@
|
|||
/* PR middle-end/94527 - Add an attribute that marks a function as freeing
|
||||
an object
|
||||
Verify that attribute malloc with one or two arguments has the expected
|
||||
effect on diagnostics.
|
||||
{ dg-options "-Wall -ftrack-macro-expansion=0" } */
|
||||
|
||||
#define A(...) __attribute__ ((malloc (__VA_ARGS__)))
|
||||
|
||||
typedef struct FILE FILE;
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
void free (void*);
|
||||
void* malloc (size_t);
|
||||
void* realloc (void*, size_t);
|
||||
|
||||
int fclose (FILE*);
|
||||
FILE* freopen (const char*, const char*, FILE*);
|
||||
int pclose (FILE*);
|
||||
|
||||
A (fclose) A (freopen, 3)
|
||||
FILE* fdopen (int);
|
||||
A (fclose) A (freopen, 3)
|
||||
FILE* fopen (const char*, const char*);
|
||||
A (fclose) A (freopen, 3)
|
||||
FILE* fmemopen(void *, size_t, const char *);
|
||||
A (fclose) A (freopen, 3)
|
||||
FILE* freopen (const char*, const char*, FILE*);
|
||||
A (pclose) A (freopen, 3)
|
||||
FILE* popen (const char*, const char*);
|
||||
A (fclose) A (freopen, 3)
|
||||
FILE* tmpfile (void);
|
||||
|
||||
void sink (FILE*);
|
||||
|
||||
|
||||
void release (void*);
|
||||
A (release) FILE* acquire (void);
|
||||
|
||||
void nowarn_fdopen (void)
|
||||
{
|
||||
{
|
||||
FILE *q = fdopen (0);
|
||||
if (!q)
|
||||
return;
|
||||
|
||||
fclose (q);
|
||||
}
|
||||
|
||||
{
|
||||
FILE *q = fdopen (0);
|
||||
if (!q)
|
||||
return;
|
||||
|
||||
q = freopen ("1", "r", q);
|
||||
fclose (q);
|
||||
}
|
||||
|
||||
{
|
||||
FILE *q = fdopen (0);
|
||||
if (!q)
|
||||
return;
|
||||
|
||||
sink (q);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void warn_fdopen (void)
|
||||
{
|
||||
{
|
||||
FILE *q = fdopen (0); // { dg-message "returned from a call to 'fdopen'" "note" }
|
||||
sink (q);
|
||||
release (q); // { dg-warning "'release' called on pointer returned from a mismatched allocation function" }
|
||||
}
|
||||
{
|
||||
FILE *q = fdopen (0); // { dg-message "returned from a call to 'fdopen'" "note" }
|
||||
sink (q);
|
||||
free (q); // { dg-warning "'free' called on pointer returned from a mismatched allocation function" }
|
||||
}
|
||||
|
||||
{
|
||||
FILE *q = fdopen (0); // { dg-message "returned from a call to 'fdopen'" "note" }
|
||||
sink (q);
|
||||
q = realloc (q, 7); // { dg-warning "'realloc' called on pointer returned from a mismatched allocation function" }
|
||||
sink (q);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void nowarn_fopen (void)
|
||||
{
|
||||
{
|
||||
FILE *q = fopen ("1", "r");
|
||||
sink (q);
|
||||
fclose (q);
|
||||
}
|
||||
|
||||
{
|
||||
FILE *q = fopen ("2", "r");
|
||||
sink (q);
|
||||
q = freopen ("3", "r", q);
|
||||
sink (q);
|
||||
fclose (q);
|
||||
}
|
||||
|
||||
{
|
||||
FILE *q = fopen ("4", "r");
|
||||
sink (q);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void warn_fopen (void)
|
||||
{
|
||||
{
|
||||
FILE *q = fopen ("1", "r");
|
||||
sink (q);
|
||||
release (q); // { dg-warning "'release' called on pointer returned from a mismatched allocation function" }
|
||||
}
|
||||
{
|
||||
FILE *q = fdopen (0);
|
||||
sink (q);
|
||||
free (q); // { dg-warning "'free' called on pointer returned from a mismatched allocation function" }
|
||||
}
|
||||
|
||||
{
|
||||
FILE *q = fdopen (0);
|
||||
sink (q);
|
||||
q = realloc (q, 7); // { dg-warning "'realloc' called on pointer returned from a mismatched allocation function" }
|
||||
sink (q);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void test_popen (void)
|
||||
{
|
||||
{
|
||||
FILE *p = popen ("1", "r");
|
||||
sink (p);
|
||||
pclose (p);
|
||||
}
|
||||
|
||||
{
|
||||
FILE *p;
|
||||
p = popen ("2", "r"); // { dg-message "returned from a call to 'popen'" "note" }
|
||||
sink (p);
|
||||
fclose (p); // { dg-warning "'fclose' called on pointer returned from a mismatched allocation function" }
|
||||
}
|
||||
|
||||
{
|
||||
/* freopen() can close a stream open by popen() but pclose() can't
|
||||
close the stream returned from freopen(). */
|
||||
FILE *p = popen ("2", "r");
|
||||
sink (p);
|
||||
p = freopen ("3", "r", p); // { dg-message "returned from a call to 'freopen'" "note" }
|
||||
sink (p);
|
||||
pclose (p); // { dg-warning "'pclose' called on pointer returned from a mismatched allocation function" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void test_tmpfile (void)
|
||||
{
|
||||
{
|
||||
FILE *p = tmpfile ();
|
||||
sink (p);
|
||||
fclose (p);
|
||||
}
|
||||
|
||||
{
|
||||
FILE *p = tmpfile ();
|
||||
sink (p);
|
||||
p = freopen ("1", "r", p);
|
||||
sink (p);
|
||||
fclose (p);
|
||||
}
|
||||
|
||||
{
|
||||
FILE *p = tmpfile (); // { dg-message "returned from a call to 'tmpfile'" "note" }
|
||||
sink (p);
|
||||
pclose (p); // { dg-warning "'pclose' called on pointer returned from a mismatched allocation function" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void warn_malloc (void)
|
||||
{
|
||||
{
|
||||
FILE *p = malloc (100); // { dg-message "returned from a call to 'malloc'" "note" }
|
||||
sink (p);
|
||||
fclose (p); // { dg-warning "'fclose' called on pointer returned from a mismatched allocation function" }
|
||||
}
|
||||
|
||||
{
|
||||
FILE *p = malloc (100); // { dg-message "returned from a call to 'malloc'" "note" }
|
||||
sink (p);
|
||||
p = freopen ("1", "r", p);// { dg-warning "'freopen' called on pointer returned from a mismatched allocation function" }
|
||||
}
|
||||
|
||||
{
|
||||
FILE *p = malloc (100); // { dg-message "returned from a call to 'malloc'" "note" }
|
||||
sink (p);
|
||||
pclose (p); // { dg-warning "'pclose' called on pointer returned from a mismatched allocation function" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void test_acquire (void)
|
||||
{
|
||||
{
|
||||
FILE *p = acquire ();
|
||||
release (p);
|
||||
}
|
||||
|
||||
{
|
||||
FILE *p = acquire ();
|
||||
sink (p);
|
||||
release (p);
|
||||
}
|
||||
|
||||
{
|
||||
FILE *p = acquire (); // { dg-message "returned from a call to 'acquire'" "note" }
|
||||
sink (p);
|
||||
fclose (p); // { dg-warning "'fclose' called on pointer returned from a mismatched allocation function" }
|
||||
}
|
||||
|
||||
{
|
||||
FILE *p = acquire (); // { dg-message "returned from a call to 'acquire'" "note" }
|
||||
sink (p);
|
||||
pclose (p); // { dg-warning "'pclose' called on pointer returned from a mismatched allocation function" }
|
||||
}
|
||||
|
||||
{
|
||||
FILE *p = acquire (); // { dg-message "returned from a call to 'acquire'" "note" }
|
||||
sink (p);
|
||||
p = freopen ("1", "r", p); // { dg-warning "'freopen' called on pointer returned from a mismatched allocation function" }
|
||||
sink (p);
|
||||
}
|
||||
|
||||
{
|
||||
FILE *p = acquire (); // { dg-message "returned from a call to 'acquire'" "note" }
|
||||
sink (p);
|
||||
free (p); // { dg-warning "'free' called on pointer returned from a mismatched allocation function" }
|
||||
}
|
||||
|
||||
{
|
||||
FILE *p = acquire (); // { dg-message "returned from a call to 'acquire'" "note" }
|
||||
sink (p);
|
||||
p = realloc (p, 123); // { dg-warning "'realloc' called on pointer returned from a mismatched allocation function" }
|
||||
sink (p);
|
||||
}
|
||||
}
|
|
@ -609,3 +609,5 @@ int test_49 (int i)
|
|||
*p = 1; /* { dg-warning "dereference of NULL 'p' \\\[CWE-476\\\]" } */
|
||||
return x;
|
||||
}
|
||||
|
||||
/* { dg-prune-output "\\\[-Wfree-nonheap-object" } */
|
||||
|
|
75
gcc/testsuite/gcc.dg/attr-malloc.c
Normal file
75
gcc/testsuite/gcc.dg/attr-malloc.c
Normal file
|
@ -0,0 +1,75 @@
|
|||
/* PR middle-end/94527 - Add an attribute that marks a function as freeing
|
||||
an object
|
||||
Verify that attribute malloc with one or two arguments is accepted where
|
||||
intended and rejected where it's invalid.
|
||||
{ dg-options "-Wall -ftrack-macro-expansion=0" } */
|
||||
|
||||
#define A(...) __attribute__ ((malloc (__VA_ARGS__)))
|
||||
|
||||
A (0) void* alloc_zero (int); // { dg-error "'malloc' attribute argument 1 does not name a function" }
|
||||
|
||||
A ("") void* alloc_string (int); // { dg-error "'malloc' attribute argument 1 does not name a function" }
|
||||
|
||||
int var;
|
||||
A (var) void* alloc_var (int); // { dg-error "'malloc' attribute argument 1 does not name a function" }
|
||||
|
||||
typedef struct Type { int i; } Type;
|
||||
A (Type) void* alloc_type (int); // { dg-error "expected expression|identifier" }
|
||||
|
||||
A (unknown) void* alloc_unknown (int); // { dg-error "'unknown' undeclared" }
|
||||
|
||||
void fv_ (); // { dg-message "declared here" }
|
||||
A (fv_) void* alloc_fv_ (int); // { dg-error "'malloc' attribute argument 1 must take a pointer type as its first argument" }
|
||||
|
||||
void fvi (int); // { dg-message "declared here" }
|
||||
A (fvi) void* alloc_fvi (int); // { dg-error "'malloc' attribute argument 1 must take a pointer type as its first argument; have 'int'" }
|
||||
|
||||
void fvv (void); // { dg-message "declared here" }
|
||||
A (fvv) void* alloc_fvv (int); // { dg-error "'malloc' attribute argument 1 must take a pointer type as its first argument; have 'void'" }
|
||||
|
||||
void fvi_ (int, ...); // { dg-message "declared here" }
|
||||
A (fvi_) void* alloc_fvi_ (int); // { dg-error "'malloc' attribute argument 1 must take a pointer type as its first argument; have 'int'" }
|
||||
|
||||
void fvi_vp (Type, void*); // { dg-message "declared here" }
|
||||
A (fvi_vp) void* alloc_fvi_vp (int); // { dg-error "'malloc' attribute argument 1 must take a pointer type as its first argument; have 'Type'" }
|
||||
|
||||
|
||||
void fpv (void*);
|
||||
A (fpv) void* alloc_fpv (int);
|
||||
|
||||
void fpv_i (void*, int);
|
||||
A (fpv_i) void* alloc_fpv_i (int);
|
||||
|
||||
void fpv_pv (void*, void*);
|
||||
A (fpv_i) void* alloc_fpv_pv (int);
|
||||
|
||||
|
||||
void gpc (char*);
|
||||
void hpi (int*);
|
||||
A (fpv) A (gpc) A (hpi) Type* alloc_fpv_gpv (int);
|
||||
|
||||
|
||||
/* Verify that the attribute can be applied to <stdio.h> functions. */
|
||||
typedef struct FILE FILE;
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
int fclose (FILE*);
|
||||
FILE* fdopen (int);
|
||||
FILE* fopen (const char*, const char*);
|
||||
FILE* freopen (const char*, const char*, FILE*);
|
||||
int pclose (FILE*);
|
||||
FILE* popen (const char*, const char*);
|
||||
FILE* tmpfile (void);
|
||||
|
||||
A (fclose) A (freopen, 3) A (pclose)
|
||||
FILE* fdopen (int);
|
||||
A (fclose) A (freopen, 3) A (pclose)
|
||||
FILE* fopen (const char*, const char*);
|
||||
A (fclose) A (freopen, 3) A (pclose)
|
||||
FILE* fmemopen(void *, size_t, const char *);
|
||||
A (fclose) A (freopen, 3) A (pclose)
|
||||
FILE* freopen (const char*, const char*, FILE*);
|
||||
A (fclose) A (freopen, 3) A (pclose)
|
||||
FILE* popen (const char*, const char*);
|
||||
A (fclose) A (freopen, 3) A (pclose)
|
||||
FILE* tmpfile (void);
|
|
@ -13,14 +13,14 @@ void foo (void)
|
|||
static char buf4[10], e;
|
||||
char *q = buf;
|
||||
free (p);
|
||||
free (q); /* { dg-warning "attempt to free a non-heap object" } */
|
||||
free (buf2); /* { dg-warning "attempt to free a non-heap object" } */
|
||||
free (&c); /* { dg-warning "attempt to free a non-heap object" } */
|
||||
free (buf3); /* { dg-warning "attempt to free a non-heap object" } */
|
||||
free (&d); /* { dg-warning "attempt to free a non-heap object" } */
|
||||
free (buf4); /* { dg-warning "attempt to free a non-heap object" } */
|
||||
free (&e); /* { dg-warning "attempt to free a non-heap object" } */
|
||||
free (q); /* { dg-warning "\\\[-Wfree-nonheap-object" } */
|
||||
free (buf2); /* { dg-warning "\\\[-Wfree-nonheap-object" } */
|
||||
free (&c); /* { dg-warning "\\\[-Wfree-nonheap-object" } */
|
||||
free (buf3); /* { dg-warning "\\\[-Wfree-nonheap-object" } */
|
||||
free (&d); /* { dg-warning "\\\[-Wfree-nonheap-object" } */
|
||||
free (buf4); /* { dg-warning "\\\[-Wfree-nonheap-object" } */
|
||||
free (&e); /* { dg-warning "\\\[-Wfree-nonheap-object" } */
|
||||
free (&r->a);
|
||||
free ("abcd"); /* { dg-warning "attempt to free a non-heap object" } */
|
||||
free (L"abcd"); /* { dg-warning "attempt to free a non-heap object" } */
|
||||
free ("abcd"); /* { dg-warning "\\\[-Wfree-nonheap-object" } */
|
||||
free (L"abcd"); /* { dg-warning "\\\[-Wfree-nonheap-object" } */
|
||||
}
|
||||
|
|
|
@ -13,14 +13,14 @@ void foo (void)
|
|||
static char buf4[10], e;
|
||||
char *q = buf;
|
||||
free (p);
|
||||
free (q); /* At -O0 no warning is reported here. */
|
||||
free (buf2); /* { dg-warning "attempt to free a non-heap object" } */
|
||||
free (&c); /* { dg-warning "attempt to free a non-heap object" } */
|
||||
free (buf3); /* { dg-warning "attempt to free a non-heap object" } */
|
||||
free (&d); /* { dg-warning "attempt to free a non-heap object" } */
|
||||
free (buf4); /* { dg-warning "attempt to free a non-heap object" } */
|
||||
free (&e); /* { dg-warning "attempt to free a non-heap object" } */
|
||||
free (q); /* { dg-warning "\\\[-Wfree-nonheap-object" } */
|
||||
free (buf2); /* { dg-warning "\\\[-Wfree-nonheap-object" } */
|
||||
free (&c); /* { dg-warning "\\\[-Wfree-nonheap-object" } */
|
||||
free (buf3); /* { dg-warning "\\\[-Wfree-nonheap-object" } */
|
||||
free (&d); /* { dg-warning "\\\[-Wfree-nonheap-object" } */
|
||||
free (buf4); /* { dg-warning "\\\[-Wfree-nonheap-object" } */
|
||||
free (&e); /* { dg-warning "\\\[-Wfree-nonheap-object" } */
|
||||
free (&r->a);
|
||||
free ("abcd"); /* { dg-warning "attempt to free a non-heap object" } */
|
||||
free (L"abcd"); /* { dg-warning "attempt to free a non-heap object" } */
|
||||
free ("abcd"); /* { dg-warning "\\\[-Wfree-nonheap-object" } */
|
||||
free (L"abcd"); /* { dg-warning "\\\[-Wfree-nonheap-object" } */
|
||||
}
|
||||
|
|
|
@ -20,3 +20,7 @@ struct ext2_icount_el *insert_icount_el() {
|
|||
ext2fs_resize_mem(&insert_icount_el_icount_1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Passing the address of a declared object to realloc triggers
|
||||
-Wfree-nonheap-object unless -flto is used.
|
||||
{ dg-prune-output "\\\[-Wfree-nonheap-object" } */
|
||||
|
|
|
@ -5,7 +5,7 @@ void test1(void)
|
|||
{
|
||||
int *p = __builtin_malloc (sizeof (int) * 4);
|
||||
*p++ = 4;
|
||||
__builtin_free (p);
|
||||
__builtin_free (p); // { dg-warning "\\\[-Wfree-nonheap-object" }
|
||||
}
|
||||
|
||||
/* Undefined. We can't do anything here. */
|
||||
|
|
|
@ -56,12 +56,12 @@ template<typename T>
|
|||
throw std::bad_alloc();
|
||||
}
|
||||
}
|
||||
return (T*)new char[n * sizeof(T)];
|
||||
return (T*)operator new (n * sizeof(T));
|
||||
}
|
||||
|
||||
void deallocate(T* p, size_type)
|
||||
{
|
||||
delete[] (char*)p;
|
||||
operator delete (p);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -94,3 +94,7 @@ int main()
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The __versa_string destructor triggers a bogus -Wfree-nonheap-object
|
||||
// due to pr54202.
|
||||
// { dg-prune-output "\\\[-Wfree-nonheap-object" }
|
||||
|
|
Loading…
Add table
Reference in a new issue