PR c++/90629 - Support for -Wmismatched-new-delete gcc/ChangeLog: PR c++/90629 * 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. (find_assignment_location): New function. (gimple_call_alloc_p): Same. (gimple_call_dealloc_argno): Same. (gimple_call_dealloc_p): Same. (matching_alloc_calls_p): Same. (warn_dealloc_offset): 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/invoke.texi (-Wfree-nonheap-object): Expand documentation. (-Wmismatched-new-delete): Document new option. gcc/c-family/ChangeLog: PR c++/90629 * c.opt (-Wmismatched-new-delete): New option. gcc/testsuite/ChangeLog: PR c++/90629 * 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. * 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. diff --git a/gcc/builtins.c b/gcc/builtins.c index 5d60eab6ba2..1b8a5b82dac 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -73,6 +73,7 @@ along with GCC; see the file COPYING3. If not see #include "gomp-constants.h" #include "omp-general.h" #include "tree-dfa.h" +#include "gimple-iterator.h" #include "gimple-ssa.h" #include "tree-ssa-live.h" #include "tree-outof-ssa.h" @@ -182,7 +183,6 @@ static rtx expand_builtin_memory_chk (tree, rtx, machine_mode, enum built_in_function); static void maybe_emit_chk_warning (tree, enum built_in_function); static void maybe_emit_sprintf_chk_warning (tree, enum built_in_function); -static void maybe_emit_free_warning (tree); static tree fold_builtin_object_size (tree, tree); static bool check_read_access (tree, tree, tree = NULL_TREE, int = 1); static bool compute_objsize (tree, int, access_ref *, bitmap *, range_query *); @@ -200,8 +200,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; @@ -5053,7 +5053,10 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited, 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)) { @@ -5154,9 +5157,11 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited, if (code == ARRAY_REF || code == MEM_REF) { + ++pref->deref; + tree ref = TREE_OPERAND (ptr, 0); tree reftype = TREE_TYPE (ref); - if (code == ARRAY_REF + if (!addr && code == ARRAY_REF && TREE_CODE (TREE_TYPE (reftype)) == POINTER_TYPE) /* Avoid arrays of pointers. FIXME: Hande pointers to arrays of known bound. */ @@ -5267,6 +5272,7 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited, if (code == STRING_CST) { pref->sizrng[0] = pref->sizrng[1] = TREE_STRING_LENGTH (ptr); + pref->ref = ptr; return true; } @@ -5276,6 +5282,10 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited, if (!compute_objsize (ref, ostype, pref, visited, rvals)) 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)) @@ -5413,9 +5423,9 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited, return true; } - if (code == ADDR_EXPR) + if (code == ADDR_EXPR || code == SSA_NAME) return compute_objsize (ptr, ostype, pref, visited, rvals); - + /* This could be an assignment from a nonlocal pointer. Save PTR to mention in diagnostics but otherwise treat it as a pointer to an unknown object. */ @@ -10281,11 +10291,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); @@ -12589,30 +12594,336 @@ 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 the location of the assignment STMT if it has one, or another + assignment on the chain that has one. Used to improve the location + of informational notes. */ -static void +static location_t +find_assignment_location (tree var) +{ + gimple *stmt = SSA_NAME_DEF_STMT (var); + + for (gimple *asgn = stmt; ; ) + { + if (gimple_has_location (asgn)) + return gimple_location (asgn); + + if (!is_gimple_assign (asgn)) + break; + + tree rhs = gimple_assign_rhs1 (asgn); + if (TREE_CODE (rhs) != SSA_NAME) + break; + + asgn = SSA_NAME_DEF_STMT (rhs); + } + + gimple_stmt_iterator gsi = gsi_for_stmt (stmt); + gsi_next_nondebug (&gsi); + if (gimple *next_stmt = gsi_stmt (gsi)) + { + if (gimple_has_location (next_stmt)) + return gimple_location (next_stmt); + } + else + { + gsi = gsi_for_stmt (stmt); + gsi_prev_nondebug (&gsi); + if (gimple *prev_stmt = gsi_stmt (gsi)) + if (gimple_has_location (prev_stmt)) + return gimple_location (prev_stmt); + } + + /* As a last resort fallback, return the location of the closing + curly brace. */ + return cfun->function_end_locus; +} + +/* 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 bool +gimple_call_alloc_p (gimple *stmt, bool all_alloc = false) +{ + tree fndecl = gimple_call_fndecl (stmt); + 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; + + /* TODO: Handle user-defined functions with attribute malloc. */ + if (!gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)) + return false; + + 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: + return false; + } +} + +/* 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 +gimple_call_dealloc_argno (gimple *stmt) +{ + tree fndecl = gimple_call_fndecl (stmt); + 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 (!gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)) + return UINT_MAX; + + switch (DECL_FUNCTION_CODE (fndecl)) + { + case BUILT_IN_FREE: + case BUILT_IN_REALLOC: + return 0; + default: + return UINT_MAX; + } +} + +/* Return true if STMT is a call to a deallocation function. */ + +static inline bool +gimple_call_dealloc_p (gimple *stmt) +{ + return gimple_call_dealloc_argno (stmt) != UINT_MAX; +} + +/* Return true if DEALLOC_DECL is a function suitable to deallocate + objects allocated by calls to ALLOC_DECL. */ + +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 false; + + /* Return true iff both functions are of the same array or + singleton form. */ + 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 !DECL_IS_OPERATOR_DELETE_P (dealloc_decl); +} + +/* 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); + if (fndecl_built_in_p (fndecl, BUILT_IN_NORMAL)) + { + built_in_function code = DECL_FUNCTION_CODE (fndecl); + if (code != BUILT_IN_FREE && code != BUILT_IN_REALLOC) + return; + } + else if (!DECL_IS_OPERATOR_DELETE_P (fndecl)) + return; - STRIP_NOPS (arg); - if (TREE_CODE (arg) != ADDR_EXPR) + if (call_expr_nargs (exp) < 1) 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, 0); + 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 = find_assignment_location (ptr); + 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 + warned = warning_at (loc, OPT_Wmismatched_new_delete, + "%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); + 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, diff --git a/gcc/builtins.h b/gcc/builtins.h index 4a2e34351be..11f2c1e3eb5 100644 --- a/gcc/builtins.h +++ b/gcc/builtins.h @@ -185,6 +185,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 () { @@ -226,6 +232,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; @@ -266,5 +275,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 */ diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 10e53ea67c9..d82899a5691 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -777,6 +777,11 @@ 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-new-delete +C++ ObjC++ Var(warn_mismatched_new_delete) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall) +Warn for deallocation calls with arguments returned from calls to mismatched +allocation functions. + Wmismatched-tags C++ ObjC++ Var(warn_mismatched_tags) Warning Warn when a class is redeclared or referenced using a mismatched class-key. diff --git a/gcc/calls.c b/gcc/calls.c index a8f459632f2..d61eb815636 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -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. diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 89168be1d2f..b50ac0a659b 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -242,7 +242,8 @@ in the following sections. -Wno-deprecated-enum-enum-conversion -Wno-deprecated-enum-float-conversion @gol -Weffc++ -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 @@ -3818,6 +3819,30 @@ 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 invocations of C++ @code{operator delete} with pointer arguments +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. 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 + +@option{-Wmismatched-new-delete} is enabled by default. + @item -Wmismatched-tags @r{(C++ and Objective-C++ only)} @opindex Wmismatched-tags @opindex Wno-mismatched-tags @@ -7699,8 +7724,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 diff --git a/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object-2.C b/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object-2.C new file mode 100644 index 00000000000..9d4d2a393ea --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object-2.C @@ -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" } +} diff --git a/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object.C b/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object.C new file mode 100644 index 00000000000..5bb0e84eb66 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object.C @@ -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; + +void* 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 (pppp); +} + +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" } +} diff --git a/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object.s b/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object.s new file mode 100644 index 00000000000..e69de29bb2d diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete.C b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete.C new file mode 100644 index 00000000000..ed1090be5c5 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete.C @@ -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" } +} diff --git a/gcc/testsuite/g++.dg/warn/delete-array-1.C b/gcc/testsuite/g++.dg/warn/delete-array-1.C index c3af71323ac..36e3637d251 100644 --- a/gcc/testsuite/g++.dg/warn/delete-array-1.C +++ b/gcc/testsuite/g++.dg/warn/delete-array-1.C @@ -6,6 +6,9 @@ struct S { int a [1]; } s; void foo (S *p) { delete a; // { dg-warning "deleting array" } + // { dg-warning "-Wfree-nonheap-object" "" { target *-*-* } .-1 } delete s.a; // { dg-warning "deleting array" } + // { dg-warning "-Wfree-nonheap-object" "" { target *-*-* } .-1 } delete p->a; // { dg-warning "deleting array" } + // { dg-warning "-Wfree-nonheap-object" "" { target *-*-* } .-1 } } diff --git a/gcc/testsuite/g++.old-deja/g++.other/delete2.C b/gcc/testsuite/g++.old-deja/g++.other/delete2.C index 1d0554f2d95..76ae3558a1d 100644 --- a/gcc/testsuite/g++.old-deja/g++.other/delete2.C +++ b/gcc/testsuite/g++.old-deja/g++.other/delete2.C @@ -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 } } diff --git a/gcc/testsuite/gcc.dg/Wfree-nonheap-object-2.c b/gcc/testsuite/gcc.dg/Wfree-nonheap-object-2.c new file mode 100644 index 00000000000..2b00d77e8b8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wfree-nonheap-object-2.c @@ -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); +} diff --git a/gcc/testsuite/gcc.dg/Wfree-nonheap-object-3.c b/gcc/testsuite/gcc.dg/Wfree-nonheap-object-3.c new file mode 100644 index 00000000000..a472b93fb87 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wfree-nonheap-object-3.c @@ -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" } +} + diff --git a/gcc/testsuite/gcc.dg/Wfree-nonheap-object.c b/gcc/testsuite/gcc.dg/Wfree-nonheap-object.c new file mode 100644 index 00000000000..bb222ccf6ab --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wfree-nonheap-object.c @@ -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" } +}