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:
Martin Sebor 2020-12-03 15:41:25 -07:00
parent a3f7a6957a
commit dce6c58db8
27 changed files with 2531 additions and 70 deletions

View file

@ -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,

View file

@ -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 */

View file

@ -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;

View file

@ -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.

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -829,7 +829,7 @@ NOINLINE static int LargeFunction(bool do_bad_access) {
x[18]++;
x[19]++;
delete x;
delete[] x;
return res;
}

View 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" }
}

View 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+]" }
}

View 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 }
}
}

View 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);

View 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" }
}

View file

@ -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" }
}

View file

@ -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 }
}

View 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);
}

View 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" }
}

View 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" }
}

View 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);
}
}

View file

@ -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" } */

View 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);

View file

@ -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" } */
}

View file

@ -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" } */
}

View file

@ -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" } */

View file

@ -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. */

View file

@ -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" }