#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"
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 &,
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;
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))
{
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
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))
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);
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);
+ unsigned argno = call_dealloc_argno (exp);
+ if ((unsigned) call_expr_nargs (exp) <= argno)
+ return;
- STRIP_NOPS (arg);
- if (TREE_CODE (arg) != ADDR_EXPR)
+ tree ptr = CALL_EXPR_ARG (exp, argno);
+ if (integer_zerop (ptr))
return;
- arg = get_base_address (TREE_OPERAND (arg, 0));
- if (arg == NULL || INDIRECT_REF_P (arg) || TREE_CODE (arg) == MEM_REF)
+ access_ref aref;
+ if (!compute_objsize (ptr, 0, &aref))
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);
+ 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,
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 ()
{
/* 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;
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 */
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 *);
{ "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,
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 }
};
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; 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)
{
- warning (OPT_Wattributes, "%qE attribute ignored", name);
*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;
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.
/* 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.
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
-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
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
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
@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
x[18]++;
x[19]++;
- delete x;
+ delete[] x;
return res;
}
--- /dev/null
+/* 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" }
+}
--- /dev/null
+/* 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+]" }
+}
--- /dev/null
+/* 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 }
+ }
+}
--- /dev/null
+/* 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);
--- /dev/null
+/* 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" }
+}
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" }
}
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 }
}
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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" }
+}
+
--- /dev/null
+/* 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" }
+}
--- /dev/null
+/* 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);
+ }
+}
*p = 1; /* { dg-warning "dereference of NULL 'p' \\\[CWE-476\\\]" } */
return x;
}
+
+/* { dg-prune-output "\\\[-Wfree-nonheap-object" } */
--- /dev/null
+/* 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);
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" } */
}
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" } */
}
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" } */
{
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. */
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);
}
};
}
}
}
+
+// The __versa_string destructor triggers a bogus -Wfree-nonheap-object
+// due to pr54202.
+// { dg-prune-output "\\\[-Wfree-nonheap-object" }