[gcc r11-5732] Add support for detecting mismatched allocation/deallocation calls.

Martin Sebor msebor@gcc.gnu.org
Thu Dec 3 22:43:49 GMT 2020


https://gcc.gnu.org/g:dce6c58db87ebf7f4477bd3126228e73e4eeee97

commit r11-5732-gdce6c58db87ebf7f4477bd3126228e73e4eeee97
Author: Martin Sebor <msebor@redhat.com>
Date:   Thu Dec 3 15:41:25 2020 -0700

    Add support for detecting mismatched allocation/deallocation calls.
    
    PR c++/90629 - Support for -Wmismatched-new-delete
    PR middle-end/94527 - Add an __attribute__ that marks a function as freeing an object
    
    gcc/ChangeLog:
    
            PR c++/90629
            PR middle-end/94527
            * builtins.c (access_ref::access_ref): Initialize new member.
            (compute_objsize): Use access_ref::deref.  Handle simple pointer
            assignment.
            (expand_builtin): Remove handling of the free built-in.
            (call_dealloc_argno): Same.
            (find_assignment_location): New function.
            (fndecl_alloc_p): Same.
            (gimple_call_alloc_p): Same.
            (call_dealloc_p): Same.
            (matching_alloc_calls_p): Same.
            (warn_dealloc_offset): Same.
            (maybe_emit_free_warning): Same.
            * builtins.h (struct access_ref): Declare new member.
            (maybe_emit_free_warning): Make extern.  Make use of access_ref.
            Handle -Wmismatched-new-delete.
            * calls.c (initialize_argument_information): Call
            maybe_emit_free_warning.
            * doc/extend.texi (attribute malloc): Update.
            * doc/invoke.texi (-Wfree-nonheap-object): Expand documentation.
            (-Wmismatched-new-delete): Document new option.
            (-Wmismatched-dealloc): Document new option.
    
    gcc/c-family/ChangeLog:
    
            PR c++/90629
            PR middle-end/94527
            * c-attribs.c (handle_dealloc_attribute): New function.
            (handle_malloc_attribute): Handle argument forms of attribute.
            * c.opt (-Wmismatched-dealloc): New option.
            (-Wmismatched-new-delete): New option.
    
    gcc/testsuite/ChangeLog:
    
            PR c++/90629
            PR middle-end/94527
            * g++.dg/asan/asan_test.cc: Fix a bug.
            * g++.dg/warn/delete-array-1.C: Add expected warning.
            * g++.old-deja/g++.other/delete2.C: Add expected warning.
            * g++.dg/warn/Wfree-nonheap-object-2.C: New test.
            * g++.dg/warn/Wfree-nonheap-object.C: New test.
            * g++.dg/warn/Wmismatched-new-delete.C: New test.
            * g++.dg/warn/Wmismatched-dealloc-2.C: New test.
            * g++.dg/warn/Wmismatched-dealloc.C: New test.
            * gcc.dg/Wmismatched-dealloc.c: New test.
            * gcc.dg/analyzer/malloc-1.c: Prune out expected warning.
            * gcc.dg/attr-malloc.c: New test.
            * gcc.dg/free-1.c: Adjust text of expected warning.
            * gcc.dg/free-2.c: Same.
            * gcc.dg/torture/pr71816.c: Prune out expected warning.
            * gcc.dg/tree-ssa/pr19831-2.c: Add an expected warning.
            * gcc.dg/Wfree-nonheap-object-2.c: New test.
            * gcc.dg/Wfree-nonheap-object-3.c: New test.
            * gcc.dg/Wfree-nonheap-object.c: New test.
    
    libstdc++-v3/ChangeLog:
    
            * testsuite/ext/vstring/modifiers/clear/56166.cc: Suppress a false
            positive warning.

Diff:
---
 gcc/builtins.c                                     | 423 +++++++++++++++++++--
 gcc/builtins.h                                     |  10 +
 gcc/c-family/c-attribs.c                           | 180 ++++++++-
 gcc/c-family/c.opt                                 |  10 +
 gcc/calls.c                                        |   4 +
 gcc/doc/extend.texi                                |  61 ++-
 gcc/doc/invoke.texi                                |  91 ++++-
 gcc/testsuite/g++.dg/asan/asan_test.cc             |   2 +-
 gcc/testsuite/g++.dg/warn/Wfree-nonheap-object-2.C | 274 +++++++++++++
 gcc/testsuite/g++.dg/warn/Wfree-nonheap-object.C   | 124 ++++++
 gcc/testsuite/g++.dg/warn/Wfree-nonheap-object.s   |   0
 gcc/testsuite/g++.dg/warn/Wmismatched-dealloc-2.C  | 185 +++++++++
 gcc/testsuite/g++.dg/warn/Wmismatched-dealloc.C    |  27 ++
 gcc/testsuite/g++.dg/warn/Wmismatched-new-delete.C | 212 +++++++++++
 gcc/testsuite/g++.dg/warn/delete-array-1.C         |   6 +-
 gcc/testsuite/g++.old-deja/g++.other/delete2.C     |   2 +
 gcc/testsuite/gcc.dg/Wfree-nonheap-object-2.c      | 279 ++++++++++++++
 gcc/testsuite/gcc.dg/Wfree-nonheap-object-3.c      |  57 +++
 gcc/testsuite/gcc.dg/Wfree-nonheap-object.c        | 273 +++++++++++++
 gcc/testsuite/gcc.dg/Wmismatched-dealloc.c         | 252 ++++++++++++
 gcc/testsuite/gcc.dg/analyzer/malloc-1.c           |   2 +
 gcc/testsuite/gcc.dg/attr-malloc.c                 |  75 ++++
 gcc/testsuite/gcc.dg/free-1.c                      |  18 +-
 gcc/testsuite/gcc.dg/free-2.c                      |  18 +-
 gcc/testsuite/gcc.dg/torture/pr71816.c             |   4 +
 gcc/testsuite/gcc.dg/tree-ssa/pr19831-2.c          |   2 +-
 .../testsuite/ext/vstring/modifiers/clear/56166.cc |   8 +-
 27 files changed, 2530 insertions(+), 69 deletions(-)

diff --git a/gcc/builtins.c b/gcc/builtins.c
index cd30de8bfb0..bd12659712f 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_r (tree, int, access_ref *, ssa_name_limit_t &,
@@ -201,8 +201,8 @@ static void expand_builtin_sync_synchronize (void);
 
 access_ref::access_ref (tree bound /* = NULL_TREE */,
 			bool minaccess /* = false */)
-: ref (), eval ([](tree x){ return x; }), trail1special (true), base0 (true),
-  parmarray ()
+: ref (), eval ([](tree x){ return x; }), deref (), trail1special (true),
+  base0 (true), parmarray ()
 {
   /* Set to valid.  */
   offrng[0] = offrng[1] = 0;
@@ -5313,7 +5313,10 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
 
   const bool addr = TREE_CODE (ptr) == ADDR_EXPR;
   if (addr)
-    ptr = TREE_OPERAND (ptr, 0);
+    {
+      --pref->deref;
+      ptr = TREE_OPERAND (ptr, 0);
+    }
 
   if (DECL_P (ptr))
     {
@@ -5421,6 +5424,8 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
 
   if (code == ARRAY_REF || code == MEM_REF)
     {
+      ++pref->deref;
+
       tree ref = TREE_OPERAND (ptr, 0);
       tree reftype = TREE_TYPE (ref);
       if (!addr && code == ARRAY_REF
@@ -5544,6 +5549,10 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
       if (!compute_objsize_r (ref, ostype, pref, snlim, qry))
 	return false;
 
+      /* Clear DEREF since the offset is being applied to the target
+	 of the dereference.  */
+      pref->deref = 0;
+
       offset_int orng[2];
       tree off = pref->eval (TREE_OPERAND (ptr, 1));
       if (get_offset_range (off, NULL, orng, rvals))
@@ -10630,11 +10639,6 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
       maybe_emit_sprintf_chk_warning (exp, fcode);
       break;
 
-    case BUILT_IN_FREE:
-      if (warn_free_nonheap_object)
-	maybe_emit_free_warning (exp);
-      break;
-
     case BUILT_IN_THREAD_POINTER:
       return expand_builtin_thread_pointer (exp, target);
 
@@ -12944,30 +12948,403 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
 		access_write_only);
 }
 
-/* Emit warning if a free is called with address of a variable.  */
+/* Return true if STMT is a call to an allocation function.  Unless
+   ALL_ALLOC is set, consider only functions that return dynmamically
+   allocated objects.  Otherwise return true even for all forms of
+   alloca (including VLA).  */
 
-static void
+static bool
+fndecl_alloc_p (tree fndecl, bool all_alloc)
+{
+  if (!fndecl)
+    return false;
+
+  /* A call to operator new isn't recognized as one to a built-in.  */
+  if (DECL_IS_OPERATOR_NEW_P (fndecl))
+    return true;
+
+  if (fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
+    {
+      switch (DECL_FUNCTION_CODE (fndecl))
+	{
+	case BUILT_IN_ALLOCA:
+	case BUILT_IN_ALLOCA_WITH_ALIGN:
+	  return all_alloc;
+	case BUILT_IN_CALLOC:
+	case BUILT_IN_MALLOC:
+	case BUILT_IN_REALLOC:
+	case BUILT_IN_STRDUP:
+	case BUILT_IN_STRNDUP:
+	  return true;
+	default:
+	  break;
+	}
+    }
+
+  /* A function is considered an allocation function if it's declared
+     with attribute malloc with an argument naming its associated
+     deallocation function.  */
+  tree attrs = DECL_ATTRIBUTES (fndecl);
+  if (!attrs)
+    return false;
+
+  for (tree allocs = attrs;
+       (allocs = lookup_attribute ("malloc", allocs));
+       allocs = TREE_CHAIN (allocs))
+    {
+      tree args = TREE_VALUE (allocs);
+      if (!args)
+	continue;
+
+      if (TREE_VALUE (args))
+	return true;
+    }
+
+  return false;
+}
+
+/* Return true if STMT is a call to an allocation function.  A wrapper
+   around fndecl_alloc_p.  */
+
+static bool
+gimple_call_alloc_p (gimple *stmt, bool all_alloc = false)
+{
+  return fndecl_alloc_p (gimple_call_fndecl (stmt), all_alloc);
+}
+
+/* Return the zero-based number corresponding to the argument being
+   deallocated if STMT is a call to a deallocation function or UINT_MAX
+   if it isn't.  */
+
+static unsigned
+call_dealloc_argno (tree exp)
+{
+  tree fndecl = get_callee_fndecl (exp);
+  if (!fndecl)
+    return UINT_MAX;
+
+  /* A call to operator delete isn't recognized as one to a built-in.  */
+  if (DECL_IS_OPERATOR_DELETE_P (fndecl))
+    return 0;
+
+  /* TODO: Handle user-defined functions with attribute malloc?  Handle
+     known non-built-ins like fopen?  */
+  if (fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
+    {
+      switch (DECL_FUNCTION_CODE (fndecl))
+	{
+	case BUILT_IN_FREE:
+	case BUILT_IN_REALLOC:
+	  return 0;
+	default:
+	  break;
+	}
+      return UINT_MAX;
+    }
+
+  tree attrs = DECL_ATTRIBUTES (fndecl);
+  if (!attrs)
+    return UINT_MAX;
+
+  for (tree atfree = attrs;
+       (atfree = lookup_attribute ("*dealloc", atfree));
+       atfree = TREE_CHAIN (atfree))
+    {
+      tree alloc = TREE_VALUE (atfree);
+      if (!alloc)
+	continue;
+
+      tree pos = TREE_CHAIN (alloc);
+      if (!pos)
+	return 0;
+
+      pos = TREE_VALUE (pos);
+      return TREE_INT_CST_LOW (pos) - 1;
+    }
+
+  return UINT_MAX;
+}
+
+/* Return true if STMT is a call to a deallocation function.  */
+
+static inline bool
+call_dealloc_p (tree exp)
+{
+  return call_dealloc_argno (exp) != UINT_MAX;
+}
+
+/* ALLOC_DECL and DEALLOC_DECL are pair of allocation and deallocation
+   functions.  Return true if the latter is suitable to deallocate objects
+   allocated by calls to the former.  */
+
+static bool
+matching_alloc_calls_p (tree alloc_decl, tree dealloc_decl)
+{
+  if (DECL_IS_OPERATOR_NEW_P (alloc_decl))
+    {
+      if (DECL_IS_OPERATOR_DELETE_P (dealloc_decl))
+	{
+	  /* Return true iff both functions are of the same array or
+	     singleton form and false otherwise.  */
+	  tree alloc_id = DECL_NAME (alloc_decl);
+	  tree dealloc_id = DECL_NAME (dealloc_decl);
+	  const char *alloc_fname = IDENTIFIER_POINTER (alloc_id);
+	  const char *dealloc_fname = IDENTIFIER_POINTER (dealloc_id);
+	  return !strchr (alloc_fname, '[') == !strchr (dealloc_fname, '[');
+	}
+
+      /* Return false for deallocation functions that are known not
+	 to match.  */
+      if (fndecl_built_in_p (dealloc_decl, BUILT_IN_FREE)
+	  || fndecl_built_in_p (dealloc_decl, BUILT_IN_REALLOC))
+	return false;
+      /* Otherwise proceed below to check the deallocation function's
+	 "*dealloc" attributes to look for one that mentions this operator
+	 new.  */
+    }
+  else if (fndecl_built_in_p (alloc_decl, BUILT_IN_NORMAL))
+    {
+      switch (DECL_FUNCTION_CODE (alloc_decl))
+	{
+	case BUILT_IN_ALLOCA:
+	case BUILT_IN_ALLOCA_WITH_ALIGN:
+	  return false;
+
+	case BUILT_IN_CALLOC:
+	case BUILT_IN_MALLOC:
+	case BUILT_IN_REALLOC:
+	case BUILT_IN_STRDUP:
+	case BUILT_IN_STRNDUP:
+	  if (DECL_IS_OPERATOR_DELETE_P (dealloc_decl))
+	    return false;
+
+	  if (fndecl_built_in_p (dealloc_decl, BUILT_IN_FREE)
+	      || fndecl_built_in_p (dealloc_decl, BUILT_IN_REALLOC))
+	    return true;
+	  break;
+
+	default:
+	  break;
+	}
+    }
+
+  /* If DEALLOC_DECL has internal "*dealloc" attribute scan the list of
+     its associated allocation functions for ALLOC_DECL.  If it's found
+     they are a matching pair, otherwise they're not.  */
+  tree attrs = DECL_ATTRIBUTES (dealloc_decl);
+  if (!attrs)
+    return false;
+
+  for (tree funs = attrs;
+       (funs = lookup_attribute ("*dealloc", funs));
+       funs = TREE_CHAIN (funs))
+    {
+      tree args = TREE_VALUE (funs);
+      if (!args)
+	continue;
+
+      tree fname = TREE_VALUE (args);
+      if (!fname)
+	continue;
+
+      if (fname == DECL_NAME (alloc_decl))
+	return true;
+    }
+
+  return false;
+}
+
+/* Return true if DEALLOC_DECL is a function suitable to deallocate
+   objectes allocated by the ALLOC call.  */
+
+static bool
+matching_alloc_calls_p (gimple *alloc, tree dealloc_decl)
+{
+  tree alloc_decl = gimple_call_fndecl (alloc);
+  if (!alloc_decl)
+    return true;
+
+  return matching_alloc_calls_p (alloc_decl, dealloc_decl);
+}
+
+/* Diagnose a call to FNDECL to deallocate a pointer referenced by
+   AREF that includes a nonzero offset.  Such a pointer cannot refer
+   to the beginning of an allocated object.  A negative offset may
+   refer to it only if the target pointer is unknown.  */
+
+static bool
+warn_dealloc_offset (location_t loc, tree exp, tree fndecl,
+		     const access_ref &aref)
+{
+  char offstr[80];
+  offstr[0] = '\0';
+  if (wi::fits_shwi_p (aref.offrng[0]))
+    {
+      if (aref.offrng[0] == aref.offrng[1]
+	  || !wi::fits_shwi_p (aref.offrng[1]))
+	sprintf (offstr, " %lli",
+		 (long long)aref.offrng[0].to_shwi ());
+      else
+	sprintf (offstr, " [%lli, %lli]",
+		 (long long)aref.offrng[0].to_shwi (),
+		 (long long)aref.offrng[1].to_shwi ());
+    }
+
+  if (!warning_at (loc, OPT_Wfree_nonheap_object,
+		   "%K%qD called on pointer %qE with nonzero offset%s",
+		   exp, fndecl, aref.ref, offstr))
+    return false;
+
+  if (DECL_P (aref.ref))
+    inform (DECL_SOURCE_LOCATION (aref.ref), "declared here");
+  else if (TREE_CODE (aref.ref) == SSA_NAME)
+    {
+      gimple *def_stmt = SSA_NAME_DEF_STMT (aref.ref);
+      if (is_gimple_call (def_stmt))
+	{
+	  tree alloc_decl = gimple_call_fndecl (def_stmt);
+	  inform (gimple_location (def_stmt),
+		  "returned from a call to %qD", alloc_decl);
+	}
+    }
+
+  return true;
+}
+
+/* Issue a warning if a deallocation function such as free, realloc,
+   or C++ operator delete is called with an argument not returned by
+   a matching allocation function such as malloc or the corresponding
+   form of C++ operatorn new.  */
+
+void
 maybe_emit_free_warning (tree exp)
 {
-  if (call_expr_nargs (exp) != 1)
+  tree fndecl = get_callee_fndecl (exp);
+  if (!fndecl)
     return;
 
-  tree arg = CALL_EXPR_ARG (exp, 0);
+  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,
diff --git a/gcc/builtins.h b/gcc/builtins.h
index 09379e85d45..642923281c1 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -220,6 +220,12 @@ struct access_ref
      argument to the minimum.  */
   offset_int size_remaining (offset_int * = NULL) const;
 
+  /* Return true if *THIS is an access to a declared object.  */
+  bool ref_declared () const
+  {
+    return DECL_P (ref) && base0 && deref < 1;
+  }
+
   /* Set the size range to the maximum.  */
   void set_max_size_range ()
   {
@@ -261,6 +267,9 @@ struct access_ref
 
   /* Used to fold integer expressions when called from front ends.  */
   tree (*eval)(tree);
+  /* Positive when REF is dereferenced, negative when its address is
+     taken.  */
+  int deref;
   /* Set if trailing one-element arrays should be treated as flexible
      array members.  */
   bool trail1special;
@@ -350,5 +359,6 @@ extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL,
 			     range_query * = NULL);
 extern bool check_access (tree, tree, tree, tree, tree,
 			  access_mode, const access_data * = NULL);
+extern void maybe_emit_free_warning (tree);
 
 #endif /* GCC_BUILTINS_H */
diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 99b663085f2..f7dad7a91d7 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -113,6 +113,7 @@ static tree handle_no_instrument_function_attribute (tree *, tree,
 static tree handle_no_profile_instrument_function_attribute (tree *, tree,
 							     tree, int, bool *);
 static tree handle_malloc_attribute (tree *, tree, tree, int, bool *);
+static tree handle_dealloc_attribute (tree *, tree, tree, int, bool *);
 static tree handle_returns_twice_attribute (tree *, tree, tree, int, bool *);
 static tree handle_no_limit_stack_attribute (tree *, tree, tree, int,
 					     bool *);
@@ -364,7 +365,7 @@ const struct attribute_spec c_common_attribute_table[] =
   { "no_profile_instrument_function",  0, 0, true, false, false, false,
 			      handle_no_profile_instrument_function_attribute,
 			      NULL },
-  { "malloc",                 0, 0, true,  false, false, false,
+  { "malloc",                 0, 2, true,  false, false, false,
 			      handle_malloc_attribute, attr_alloc_exclusions },
   { "returns_twice",          0, 0, true,  false, false, false,
 			      handle_returns_twice_attribute,
@@ -524,6 +525,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_objc_root_class_attribute, NULL },
   { "objc_nullability",	      1, 1, true, false, false, false,
 			      handle_objc_nullability_attribute, NULL },
+  { "*dealloc",                1, 2, true, false, false, false,
+			      handle_dealloc_attribute, NULL },
   { NULL,                     0, 0, false, false, false, false, NULL, NULL }
 };
 
@@ -3127,20 +3130,179 @@ handle_no_profile_instrument_function_attribute (tree *node, tree name, tree,
   return NULL_TREE;
 }
 
-/* Handle a "malloc" attribute; arguments as in
-   struct attribute_spec.handler.  */
+/* Handle the "malloc" attribute.  */
 
 static tree
-handle_malloc_attribute (tree *node, tree name, tree ARG_UNUSED (args),
+handle_malloc_attribute (tree *node, tree name, tree args,
 			 int ARG_UNUSED (flags), bool *no_add_attrs)
 {
-  if (TREE_CODE (*node) == FUNCTION_DECL
-      && POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (*node))))
-    DECL_IS_MALLOC (*node) = 1;
-  else
+  tree fndecl = *node;
+
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored; 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;
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 059f6c38a40..79478285070 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -793,6 +793,16 @@ Wmisleading-indentation
 C C++ Common Var(warn_misleading_indentation) Warning LangEnabledBy(C C++,Wall)
 Warn when the indentation of the code does not reflect the block structure.
 
+Wmismatched-dealloc
+C ObjC C++ ObjC++ Var(warn_mismatched_alloc) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
+Warn for deallocation calls with arguments returned from mismatched allocation
+functions.
+
+Wmismatched-new-delete
+C++ ObjC++ Var(warn_mismatched_new_delete) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
+Warn for mismatches between calls to operator new or delete and the corrsponding
+call to the allocation or deallocation function.
+
 Wmismatched-tags
 C++ ObjC++ Var(warn_mismatched_tags) Warning
 Warn when a class is redeclared or referenced using a mismatched class-key.
diff --git a/gcc/calls.c b/gcc/calls.c
index a93d4bf0787..4114bf5e5b2 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/extend.texi b/gcc/doc/extend.texi
index 195bb21068a..435761572cd 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -3233,20 +3233,63 @@ this reason the attribute is not allowed on types to annotate indirect
 calls.
 
 @item malloc
+@item malloc (@var{deallocator})
+@item malloc (@var{deallocator}, @var{ptr-index})
 @cindex @code{malloc} function attribute
 @cindex functions that behave like malloc
-This tells the compiler that a function is @code{malloc}-like, i.e.,
-that the pointer @var{P} returned by the function cannot alias any
+Attribute @code{malloc} indicates that a function is @code{malloc}-like,
+i.e., that the pointer @var{P} returned by the function cannot alias any
 other pointer valid when the function returns, and moreover no
 pointers to valid objects occur in any storage addressed by @var{P}.
 
-Using this attribute can improve optimization.  Compiler predicts
-that a function with the attribute returns non-null in most cases.
-Functions like
-@code{malloc} and @code{calloc} have this property because they return
-a pointer to uninitialized or zeroed-out storage.  However, functions
-like @code{realloc} do not have this property, as they can return a
-pointer to storage containing pointers.
+Independently, the form of the attribute with one or two arguments
+associates @code{deallocator} as a suitable deallocation function for
+pointers returned from the @code{malloc}-like function.  @var{ptr-index}
+denotes the positional argument to which when the pointer is passed in
+calls to @code{deallocator} has the effect of deallocating it.
+
+Using the attribute with no arguments is designed to improve optimization.
+The compiler predicts that a function with the attribute returns non-null
+in most cases.  Functions like @code{malloc} and @code{calloc} have this
+property because they return a pointer to uninitialized or zeroed-out
+storage.  However, functions like @code{realloc} do not have this property,
+as they may return pointers to storage containing pointers to existing
+objects.
+
+Associating a function with a @var{deallocator} helps detect calls to
+mismatched allocation and deallocation functions and diagnose them
+under the control of options such as @option{-Wmismatched-dealloc}.
+To indicate that an allocation function both satisifies the nonaliasing
+property and has a deallocator associated with it, both the plain form
+of the attribute and the one with the @var{deallocator} argument must
+be used.
+
+For example, besides stating that the functions return pointers that do
+not alias any others, the following declarations make the @code{fclose}
+and @code{frepen} functions suitable deallocators for pointers returned
+from all the functions that return them, and the @code{pclose} function
+as the only other suitable deallocator besides @code{freopen} for pointers
+returned from @code{popen}.  The deallocator functions must declared
+before they can be referenced in the attribute.
+
+@smallexample
+int   fclose (FILE*);
+FILE* freopen (const char*, const char*, FILE*);
+int   pclose (FILE*);
+
+__attribute__ ((malloc, malloc (fclose), malloc (freopen, 3)))
+  FILE* fdopen (int);
+__attribute__ ((malloc, malloc (fclose), malloc (freopen, 3)))
+  FILE* fopen (const char*, const char*);
+__attribute__ ((malloc, malloc (fclose), malloc (freopen, 3)))
+  FILE* fmemopen(void *, size_t, const char *);
+__attribute__ ((malloc, malloc (fclose), malloc (freopen, 3)))
+  FILE* freopen (const char*, const char*, FILE*);
+__attribute__ ((malloc, malloc (pclose), malloc (freopen, 3)))
+  FILE* popen (const char*, const char*);
+__attribute__ ((malloc, malloc (fclose), malloc (freopen, 3)))
+  FILE* tmpfile (void);
+@end smallexample
 
 @item no_icf
 @cindex @code{no_icf} function attribute
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 35cd3dcd317..615eae9a1c5 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++  -Wno-exceptions -Wextra-semi  -Wno-inaccessible-base @gol
 -Wno-inherited-variadic-ctor  -Wno-init-list-lifetime @gol
--Wno-invalid-offsetof  -Wno-literal-suffix  -Wmismatched-tags @gol
+-Wno-invalid-offsetof  -Wno-literal-suffix @gol
+-Wno-mismatched-new-delete -Wmismatched-tags @gol
 -Wmultiple-inheritance  -Wnamespaces  -Wnarrowing @gol
 -Wnoexcept  -Wnoexcept-type  -Wnon-virtual-dtor @gol
 -Wpessimizing-move  -Wno-placement-new  -Wplacement-new=@var{n} @gol
@@ -3859,6 +3860,40 @@ The warning is inactive inside a system header file, such as the STL, so
 one can still use the STL.  One may also instantiate or specialize
 templates.
 
+@item -Wno-mismatched-new-delete @r{(C++ and Objective-C++ only)}
+@opindex Wmismatched-new-delete
+@opindex Wno-mismatched-new-delete
+Warn for mismatches between calls to @code{operator new} or @code{operator
+delete} and the corresponding call to the allocation or deallocation function.
+This includes invocations of C++ @code{operator delete} with pointers
+returned from either mismatched forms of @code{operator new}, or from other
+functions that allocate objects for which the @code{operator delete} isn't
+a suitable deallocator, as well as calls to other deallocation functions
+with pointers returned from @code{operator new} for which the deallocation
+function isn't suitable.
+
+For example, the @code{delete} expression in the function below is diagnosed
+because it doesn't match the array form of the @code{new} expression
+the pointer argument was returned from.  Similarly, the call to @code{free}
+is also diagnosed.
+
+@smallexample
+void f ()
+@{
+  int *a = new int[n];
+  delete a;   // warning: mismatch in array forms of expressions
+
+  char *p = new char[n];
+  free (p);   // warning: mismatch between new and free
+@}
+@end smallexample
+
+The related option @option{-Wmismatched-dealloc} diagnoses mismatches
+involving allocation and deallocation functions other than @code{operator
+new} and @code{operator delete}.
+
+@option{-Wmismatched-new-delete} is enabled by default.
+
 @item -Wmismatched-tags @r{(C++ and Objective-C++ only)}
 @opindex Wmismatched-tags
 @opindex Wno-mismatched-tags
@@ -6287,6 +6322,41 @@ Ignoring the warning can result in poorly optimized code.
 disable the warning, but this is not recommended and should be done only
 when non-existent profile data is justified.
 
+@item -Wno-mismatched-dealloc
+@opindex Wmismatched-dealloc
+@opindex Wno-mismatched-dealloc
+
+Warn for calls to deallocation functions with pointer arguments returned
+from from allocations functions for which the former isn't a suitable
+deallocator.  A pair of functions can be associated as matching allocators
+and deallocators by use of attribute @code{malloc}.  Unless disabled by
+the @option{-fno-builtin} option the standard functions @code{calloc},
+@code{malloc}, @code{realloc}, and @code{free}, as well as the corresponding
+forms of C++ @code{operator new} and @code{operator delete} are implicitly
+associated as matching allocators and deallocators.  In the following
+example @code{mydealloc} is the deallocator for pointers returned from
+@code{myalloc}.
+
+@smallexample
+void mydealloc (void*);
+
+__attribute__ ((malloc (mydealloc, 1))) void*
+myalloc (size_t);
+
+void f (void)
+@{
+  void *p = myalloc (32);
+  // @dots{}use p@dots{}
+  free (p);   // warning: not a matching deallocator for myalloc
+  mydealloc (p);   // ok
+@}
+@end smallexample
+
+In C++, the related option @option{-Wmismatched-new-delete} diagnoses
+mismatches involving either @code{operator new} or @code{operator delete}.
+
+Option @option{-Wmismatched-dealloc} is enabled by default.
+
 @item -Wmultistatement-macros
 @opindex Wmultistatement-macros
 @opindex Wno-multistatement-macros
@@ -7778,8 +7848,23 @@ to @option{-Wframe-larger-than=}@samp{SIZE_MAX} or larger.
 @item -Wno-free-nonheap-object
 @opindex Wno-free-nonheap-object
 @opindex Wfree-nonheap-object
-Do not warn when attempting to free an object that was not allocated
-on the heap.
+Warn when attempting to deallocate an object that was either not allocated
+on the heap, or by using a pointer that was not returned from a prior call
+to the corresponding allocation function.  For example, because the call
+to @code{stpcpy} returns a pointer to the terminating nul character and
+not to the begginning of the object, the call to @code{free} below is
+diagnosed.
+
+@smallexample
+void f (char *p)
+@{
+  p = stpcpy (p, "abc");
+  // ...
+  free (p);   // warning
+@}
+@end smallexample
+
+@option{-Wfree-nonheap-object} is enabled by default.
 
 @item -Wstack-usage=@var{byte-size}
 @opindex Wstack-usage
diff --git a/gcc/testsuite/g++.dg/asan/asan_test.cc b/gcc/testsuite/g++.dg/asan/asan_test.cc
index 5f2e2c244dd..dbf1a6ac0a5 100644
--- a/gcc/testsuite/g++.dg/asan/asan_test.cc
+++ b/gcc/testsuite/g++.dg/asan/asan_test.cc
@@ -829,7 +829,7 @@ NOINLINE static int LargeFunction(bool do_bad_access) {
   x[18]++;
   x[19]++;
 
-  delete x;
+  delete[] x;
   return res;
 }
 
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..82b081ad48f
--- /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;
+
+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+]" }
+}
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-dealloc-2.C b/gcc/testsuite/g++.dg/warn/Wmismatched-dealloc-2.C
new file mode 100644
index 00000000000..7ecc99a325c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-dealloc-2.C
@@ -0,0 +1,185 @@
+/* PR middle-end/94527 - Add an attribute that marks a function as freeing
+   an object
+   The detection doesn't require optimization.
+   { dg-do compile }
+   { dg-options "-Wall" } */
+
+#define A(...) __attribute__ ((malloc (__VA_ARGS__)))
+
+typedef __SIZE_TYPE__ size_t;
+
+extern "C" {
+  void free (void *);
+  void* realloc (void *, size_t);
+}
+
+void sink (void *);
+
+void                   mydealloc (int, void*);
+void* A (mydealloc, 2) myalloc (void*);
+
+
+void my_delete (const char*, void*);
+void my_array_delete (const char*, void*);
+
+typedef void OpDelete1 (void*);
+typedef void OpDelete2 (void*, size_t);
+
+A ((OpDelete1*)operator delete, 1)
+#if __cplusplus >= 201402L
+A ((OpDelete2*)operator delete, 1)
+#endif
+A (my_delete, 2)
+int* my_new (size_t);
+
+A ((OpDelete1*)operator delete[], 1)
+#if __cplusplus >= 201402L
+A ((OpDelete2*)operator delete[], 1)
+#endif
+A (my_array_delete, 2)
+int* my_array_new (size_t);
+
+
+void test_my_new ()
+{
+  {
+    void *p = my_new (1);
+    operator delete (p);
+  }
+  {
+    void *p = my_new (1);
+    sink (p);
+    operator delete (p);
+  }
+  {
+    int *p = my_new (1);
+    sink (p);
+    delete p;
+  }
+
+  {
+    void *p = my_new (1);
+    // { dg-message "returned from a call to 'int\\\* my_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
+    operator delete[] (p);
+    // { dg-warning "'void operator delete \\\[]\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function \\\[-Wmismatched-new-delete" "" { target *-*-* } .-1 }
+  }
+  {
+    void *p = my_new (1);
+    // { dg-message "returned from a call to 'int\\\* my_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
+    sink (p);
+    operator delete[] (p);
+    // { dg-warning "'void operator delete \\\[]\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function \\\[-Wmismatched-new-delete" "" { target *-*-* } .-1 }
+  }
+  {
+    int *p = my_new (1);
+    sink (p);
+    delete[] p;
+    // { dg-warning "'void operator delete \\\[]\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function \\\[-Wmismatched-new-delete" "" { target *-*-* } .-1 }
+  }
+
+  {
+    void *p = my_new (1);
+    my_delete ("1", p);
+  }
+  {
+    void *p = my_new (1);
+    sink (p);
+    my_delete ("2", p);
+  }
+
+  {
+    void *p = my_new (1);
+    // { dg-message "returned from a call to 'int\\\* my_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
+    sink (p);
+    my_array_delete ("3", p);
+    // { dg-warning "'void my_array_delete\\\(const char\\\*, void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
+  }
+
+  {
+    void *p = my_new (1);
+    // { dg-message "returned from a call to 'int\\\* my_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
+    sink (p);
+    free (p);
+    // { dg-warning "'void free\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
+  }
+
+  {
+    void *p = my_new (1);
+    // { dg-message "returned from a call to 'int\\\* my_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
+    sink (p);
+    p = realloc (p, 123);
+    // { dg-warning "'void\\\* realloc\\\(void\\\*, size_t\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
+  }
+}
+
+
+void test_my_array_new ()
+{
+  {
+    void *p = my_array_new (1);
+    operator delete[] (p);
+  }
+  {
+    void *p = my_array_new (1);
+    sink (p);
+    operator delete[] (p);
+  }
+  {
+    int *p = my_array_new (1);
+    sink (p);
+    delete[] p;
+  }
+
+  {
+    void *p = my_array_new (1);
+    // { dg-message "returned from a call to 'int\\\* my_array_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
+    operator delete (p);
+    // { dg-warning "'void operator delete\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function \\\[-Wmismatched-new-delete" "" { target *-*-* } .-1 }
+  }
+  {
+    void *p = my_array_new (1);
+    // { dg-message "returned from a call to 'int\\\* my_array_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
+    sink (p);
+    operator delete (p);
+    // { dg-warning "'void operator delete\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function \\\[-Wmismatched-new-delete" "" { target *-*-* } .-1 }
+  }
+  {
+    int *p = my_array_new (1);
+    sink (p);
+    delete p;
+    // { dg-warning "'void operator delete\\\(void\\\*\[^\)\]*\\\)' called on pointer returned from a mismatched allocation function \\\[-Wmismatched-new-delete" "" { target *-*-* } .-1 }
+  }
+
+  {
+    void *p = my_array_new (1);
+    my_array_delete ("1", p);
+  }
+  {
+    void *p = my_array_new (1);
+    sink (p);
+    my_array_delete ("2", p);
+  }
+  {
+    void *p = my_array_new (1);
+    // { dg-message "returned from a call to 'int\\\* my_array_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
+    sink (p);
+    my_delete ("3", p);
+    // { dg-warning "'void my_delete\\\(const char\\\*, void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
+  }
+
+  {
+    void *p = my_array_new (1);
+    // { dg-message "returned from a call to 'int\\\* my_array_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
+    sink (p);
+    free (p);
+    // { dg-warning "'void free\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
+  }
+
+  {
+    void *p = my_array_new (1);
+    // { dg-message "returned from a call to 'int\\\* my_array_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
+    sink (p);
+    p = realloc (p, 123);
+    // { dg-warning "'void\\\* realloc\\\(void\\\*, size_t\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
+  }
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-dealloc.C b/gcc/testsuite/g++.dg/warn/Wmismatched-dealloc.C
new file mode 100644
index 00000000000..682db6f02cb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-dealloc.C
@@ -0,0 +1,27 @@
+/* PR middle-end/94527 - Add an attribute that marks a function as freeing
+   an object
+   { dg-do compile { target c++11 } }
+   { dg-options "-Wall" } */
+
+#define A(...) __attribute__ ((malloc (__VA_ARGS__)))
+
+typedef __SIZE_TYPE__ size_t;
+
+void                   mydealloc (int, void*);
+void* A (mydealloc, 2) myalloc (void*);
+
+
+void* A (operator delete, 1)
+  bad_new (size_t);                     // { dg-error "attribute argument 1 is ambiguous" }
+void* A (operator delete[], 1)
+  bad_array_new (size_t);               // { dg-error "attribute argument 1 is ambiguous" }
+
+void my_delete (const char*, void*);
+void my_array_delete (const char*, void*);
+
+typedef void OpDelete (void*);
+
+int* A ((OpDelete*)operator delete, 1) A (my_delete, 2)
+  my_new (size_t);
+int* A ((OpDelete*)operator delete[], 1) A (my_array_delete, 2)
+  my_array_new (size_t);
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..95fa7d445d4 100644
--- a/gcc/testsuite/g++.dg/warn/delete-array-1.C
+++ b/gcc/testsuite/g++.dg/warn/delete-array-1.C
@@ -5,7 +5,7 @@ struct S { int a [1]; } s;
 
 void foo (S *p)
 {
-  delete a;    // { dg-warning "deleting array" }
-  delete s.a;  // { dg-warning "deleting array" }
-  delete p->a; // { dg-warning "deleting array" }
+  delete a;    // { dg-warning "deleting array|-Wfree-nonheap-object" }
+  delete s.a;  // { dg-warning "deleting array|-Wfree-nonheap-object" }
+  delete p->a; // { dg-warning "deleting array|-Wfree-nonheap-object" }
 }
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" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wmismatched-dealloc.c b/gcc/testsuite/gcc.dg/Wmismatched-dealloc.c
new file mode 100644
index 00000000000..7c5d6acf4d6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wmismatched-dealloc.c
@@ -0,0 +1,252 @@
+/* PR middle-end/94527 - Add an attribute that marks a function as freeing
+   an object
+   Verify that attribute malloc with one or two arguments has the expected
+   effect on diagnostics.
+   { dg-options "-Wall -ftrack-macro-expansion=0" } */
+
+#define A(...) __attribute__ ((malloc (__VA_ARGS__)))
+
+typedef struct FILE   FILE;
+typedef __SIZE_TYPE__ size_t;
+
+void  free (void*);
+void* malloc (size_t);
+void* realloc (void*, size_t);
+
+int   fclose (FILE*);
+FILE* freopen (const char*, const char*, FILE*);
+int   pclose (FILE*);
+
+A (fclose) A (freopen, 3)
+  FILE* fdopen (int);
+A (fclose) A (freopen, 3)
+  FILE* fopen (const char*, const char*);
+A (fclose) A (freopen, 3)
+  FILE* fmemopen(void *, size_t, const char *);
+A (fclose) A (freopen, 3)
+  FILE* freopen (const char*, const char*, FILE*);
+A (pclose) A (freopen, 3)
+  FILE* popen (const char*, const char*);
+A (fclose) A (freopen, 3)
+  FILE* tmpfile (void);
+
+void sink (FILE*);
+
+
+            void  release (void*);
+A (release) FILE* acquire (void);
+
+void nowarn_fdopen (void)
+{
+  {
+    FILE *q = fdopen (0);
+    if (!q)
+      return;
+
+    fclose (q);
+  }
+
+  {
+    FILE *q = fdopen (0);
+    if (!q)
+      return;
+
+    q = freopen ("1", "r", q);
+    fclose (q);
+  }
+
+  {
+    FILE *q = fdopen (0);
+    if (!q)
+      return;
+
+    sink (q);
+  }
+}
+
+
+void warn_fdopen (void)
+{
+  {
+    FILE *q = fdopen (0);     // { dg-message "returned from a call to 'fdopen'" "note" }
+    sink (q);
+    release (q);              // { dg-warning "'release' called on pointer returned from a mismatched allocation function" }
+  }
+  {
+    FILE *q = fdopen (0);     // { dg-message "returned from a call to 'fdopen'" "note" }
+    sink (q);
+    free (q);                 // { dg-warning "'free' called on pointer returned from a mismatched allocation function" }
+  }
+
+  {
+    FILE *q = fdopen (0);     // { dg-message "returned from a call to 'fdopen'" "note" }
+    sink (q);
+    q = realloc (q, 7);       // { dg-warning "'realloc' called on pointer returned from a mismatched allocation function" }
+    sink (q);
+  }
+}
+
+
+void nowarn_fopen (void)
+{
+  {
+    FILE *q = fopen ("1", "r");
+    sink (q);
+    fclose (q);
+  }
+
+  {
+    FILE *q = fopen ("2", "r");
+    sink (q);
+    q = freopen ("3", "r", q);
+    sink (q);
+    fclose (q);
+  }
+
+  {
+    FILE *q = fopen ("4", "r");
+    sink (q);
+  }
+}
+
+
+void warn_fopen (void)
+{
+  {
+    FILE *q = fopen ("1", "r");
+    sink (q);
+    release (q);              // { dg-warning "'release' called on pointer returned from a mismatched allocation function" }
+  }
+  {
+    FILE *q = fdopen (0);
+    sink (q);
+    free (q);                 // { dg-warning "'free' called on pointer returned from a mismatched allocation function" }
+  }
+
+  {
+    FILE *q = fdopen (0);
+    sink (q);
+    q = realloc (q, 7);       // { dg-warning "'realloc' called on pointer returned from a mismatched allocation function" }
+    sink (q);
+  }
+}
+
+
+void test_popen (void)
+{
+  {
+    FILE *p = popen ("1", "r");
+    sink (p);
+    pclose (p);
+  }
+
+  {
+    FILE *p;
+    p = popen ("2", "r");     // { dg-message "returned from a call to 'popen'" "note" }
+    sink (p);
+    fclose (p);               // { dg-warning "'fclose' called on pointer returned from a mismatched allocation function" }
+  }
+
+  {
+    /* freopen() can close a stream open by popen() but pclose() can't
+       close the stream returned from freopen().  */
+    FILE *p = popen ("2", "r");
+    sink (p);
+    p = freopen ("3", "r", p);  // { dg-message "returned from a call to 'freopen'" "note" }
+    sink (p);
+    pclose (p);               // { dg-warning "'pclose' called on pointer returned from a mismatched allocation function" }
+  }
+}
+
+
+void test_tmpfile (void)
+{
+  {
+    FILE *p = tmpfile ();
+    sink (p);
+    fclose (p);
+  }
+
+  {
+    FILE *p = tmpfile ();
+    sink (p);
+    p = freopen ("1", "r", p);
+    sink (p);
+    fclose (p);
+  }
+
+  {
+    FILE *p = tmpfile ();     // { dg-message "returned from a call to 'tmpfile'" "note" }
+    sink (p);
+    pclose (p);               // { dg-warning "'pclose' called on pointer returned from a mismatched allocation function" }
+  }
+}
+
+
+void warn_malloc (void)
+{
+  {
+    FILE *p = malloc (100);   // { dg-message "returned from a call to 'malloc'" "note" }
+    sink (p);
+    fclose (p);               // { dg-warning "'fclose' called on pointer returned from a mismatched allocation function" }
+  }
+
+  {
+    FILE *p = malloc (100);   // { dg-message "returned from a call to 'malloc'" "note" }
+    sink (p);
+    p = freopen ("1", "r", p);// { dg-warning "'freopen' called on pointer returned from a mismatched allocation function" }
+  }
+
+  {
+    FILE *p = malloc (100);   // { dg-message "returned from a call to 'malloc'" "note" }
+    sink (p);
+    pclose (p);               // { dg-warning "'pclose' called on pointer returned from a mismatched allocation function" }
+  }
+}
+
+
+void test_acquire (void)
+{
+  {
+    FILE *p = acquire ();
+    release (p);
+  }
+
+  {
+    FILE *p = acquire ();
+    sink (p);
+    release (p);
+  }
+
+  {
+    FILE *p = acquire ();     // { dg-message "returned from a call to 'acquire'" "note" }
+    sink (p);
+    fclose (p);               // { dg-warning "'fclose' called on pointer returned from a mismatched allocation function" }
+  }
+
+  {
+    FILE *p = acquire ();     // { dg-message "returned from a call to 'acquire'" "note" }
+    sink (p);
+    pclose (p);               // { dg-warning "'pclose' called on pointer returned from a mismatched allocation function" }
+  }
+
+  {
+    FILE *p = acquire ();     // { dg-message "returned from a call to 'acquire'" "note" }
+    sink (p);
+    p = freopen ("1", "r", p);  // { dg-warning "'freopen' called on pointer returned from a mismatched allocation function" }
+    sink (p);
+  }
+
+  {
+    FILE *p = acquire ();     // { dg-message "returned from a call to 'acquire'" "note" }
+    sink (p);
+    free (p);               // { dg-warning "'free' called on pointer returned from a mismatched allocation function" }
+  }
+
+  {
+    FILE *p = acquire ();     // { dg-message "returned from a call to 'acquire'" "note" }
+    sink (p);
+    p = realloc (p, 123);     // { dg-warning "'realloc' called on pointer returned from a mismatched allocation function" }
+    sink (p);
+  }
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-1.c b/gcc/testsuite/gcc.dg/analyzer/malloc-1.c
index c5bf1227c55..26d828848a2 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-1.c
@@ -609,3 +609,5 @@ int test_49 (int i)
   *p = 1; /* { dg-warning "dereference of NULL 'p' \\\[CWE-476\\\]" } */
   return x;
 }
+
+/* { dg-prune-output "\\\[-Wfree-nonheap-object" } */
diff --git a/gcc/testsuite/gcc.dg/attr-malloc.c b/gcc/testsuite/gcc.dg/attr-malloc.c
new file mode 100644
index 00000000000..14f1980ed7f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-malloc.c
@@ -0,0 +1,75 @@
+/* PR middle-end/94527 - Add an attribute that marks a function as freeing
+   an object
+   Verify that attribute malloc with one or two arguments is accepted where
+   intended and rejected where it's invalid.
+   { dg-options "-Wall -ftrack-macro-expansion=0" } */
+
+#define A(...) __attribute__ ((malloc (__VA_ARGS__)))
+
+A (0) void* alloc_zero (int);           // { dg-error "'malloc' attribute argument 1 does not name a function" }
+
+A ("") void* alloc_string (int);        // { dg-error "'malloc' attribute argument 1 does not name a function" }
+
+int var;
+A (var) void* alloc_var (int);          // { dg-error "'malloc' attribute argument 1 does not name a function" }
+
+typedef struct Type { int i; } Type;
+A (Type) void* alloc_type (int);        // { dg-error "expected expression|identifier" }
+
+A (unknown) void* alloc_unknown (int);  // { dg-error "'unknown' undeclared" }
+
+void fv_ ();                            // { dg-message "declared here" }
+A (fv_) void* alloc_fv_ (int);          // { dg-error "'malloc' attribute argument 1 must take a pointer type as its first argument" }
+
+void fvi (int);                         // { dg-message "declared here" }
+A (fvi) void* alloc_fvi (int);          // { dg-error "'malloc' attribute argument 1 must take a pointer type as its first argument; have 'int'" }
+
+void fvv (void);                        // { dg-message "declared here" }
+A (fvv) void* alloc_fvv (int);          // { dg-error "'malloc' attribute argument 1 must take a pointer type as its first argument; have 'void'" }
+
+void fvi_ (int, ...);                   // { dg-message "declared here" }
+A (fvi_) void* alloc_fvi_ (int);        // { dg-error "'malloc' attribute argument 1 must take a pointer type as its first argument; have 'int'" }
+
+void fvi_vp (Type, void*);              // { dg-message "declared here" }
+A (fvi_vp) void* alloc_fvi_vp (int);    // { dg-error "'malloc' attribute argument 1 must take a pointer type as its first argument; have 'Type'" }
+
+
+void fpv (void*);
+A (fpv) void* alloc_fpv (int);
+
+void fpv_i (void*, int);
+A (fpv_i) void* alloc_fpv_i (int);
+
+void fpv_pv (void*, void*);
+A (fpv_i) void* alloc_fpv_pv (int);
+
+
+void gpc (char*);
+void hpi (int*);
+A (fpv) A (gpc) A (hpi) Type* alloc_fpv_gpv (int);
+
+
+/* Verify that the attribute can be applied to <stdio.h> functions.  */
+typedef struct FILE FILE;
+typedef __SIZE_TYPE__ size_t;
+
+int   fclose (FILE*);
+FILE* fdopen (int);
+FILE* fopen (const char*, const char*);
+FILE* freopen (const char*, const char*, FILE*);
+int   pclose (FILE*);
+FILE* popen (const char*, const char*);
+FILE* tmpfile (void);
+
+A (fclose) A (freopen, 3) A (pclose)
+  FILE* fdopen (int);
+A (fclose) A (freopen, 3) A (pclose)
+  FILE* fopen (const char*, const char*);
+A (fclose) A (freopen, 3) A (pclose)
+  FILE* fmemopen(void *, size_t, const char *);
+A (fclose) A (freopen, 3) A (pclose)
+  FILE* freopen (const char*, const char*, FILE*);
+A (fclose) A (freopen, 3) A (pclose)
+  FILE* popen (const char*, const char*);
+A (fclose) A (freopen, 3) A (pclose)
+  FILE* tmpfile (void);
diff --git a/gcc/testsuite/gcc.dg/free-1.c b/gcc/testsuite/gcc.dg/free-1.c
index 5496c84fdb8..ad49d787fbe 100644
--- a/gcc/testsuite/gcc.dg/free-1.c
+++ b/gcc/testsuite/gcc.dg/free-1.c
@@ -13,14 +13,14 @@ void foo (void)
   static char buf4[10], e;
   char *q = buf;
   free (p);
-  free (q);	      /* { dg-warning "attempt to free a non-heap object" } */
-  free (buf2);	      /* { dg-warning "attempt to free a non-heap object" } */
-  free (&c);	      /* { dg-warning "attempt to free a non-heap object" } */
-  free (buf3);	      /* { dg-warning "attempt to free a non-heap object" } */
-  free (&d);	      /* { dg-warning "attempt to free a non-heap object" } */
-  free (buf4);	      /* { dg-warning "attempt to free a non-heap object" } */
-  free (&e);	      /* { dg-warning "attempt to free a non-heap object" } */
+  free (q);	      /* { dg-warning "\\\[-Wfree-nonheap-object" } */
+  free (buf2);	      /* { dg-warning "\\\[-Wfree-nonheap-object" } */
+  free (&c);	      /* { dg-warning "\\\[-Wfree-nonheap-object" } */
+  free (buf3);	      /* { dg-warning "\\\[-Wfree-nonheap-object" } */
+  free (&d);	      /* { dg-warning "\\\[-Wfree-nonheap-object" } */
+  free (buf4);	      /* { dg-warning "\\\[-Wfree-nonheap-object" } */
+  free (&e);	      /* { dg-warning "\\\[-Wfree-nonheap-object" } */
   free (&r->a);
-  free ("abcd");      /* { dg-warning "attempt to free a non-heap object" } */
-  free (L"abcd");     /* { dg-warning "attempt to free a non-heap object" } */
+  free ("abcd");      /* { dg-warning "\\\[-Wfree-nonheap-object" } */
+  free (L"abcd");     /* { dg-warning "\\\[-Wfree-nonheap-object" } */
 }
diff --git a/gcc/testsuite/gcc.dg/free-2.c b/gcc/testsuite/gcc.dg/free-2.c
index eb94651311b..edbcdc74a20 100644
--- a/gcc/testsuite/gcc.dg/free-2.c
+++ b/gcc/testsuite/gcc.dg/free-2.c
@@ -13,14 +13,14 @@ void foo (void)
   static char buf4[10], e;
   char *q = buf;
   free (p);
-  free (q);	      /* At -O0 no warning is reported here.  */
-  free (buf2);	      /* { dg-warning "attempt to free a non-heap object" } */
-  free (&c);	      /* { dg-warning "attempt to free a non-heap object" } */
-  free (buf3);	      /* { dg-warning "attempt to free a non-heap object" } */
-  free (&d);	      /* { dg-warning "attempt to free a non-heap object" } */
-  free (buf4);	      /* { dg-warning "attempt to free a non-heap object" } */
-  free (&e);	      /* { dg-warning "attempt to free a non-heap object" } */
+  free (q);	      /* { dg-warning "\\\[-Wfree-nonheap-object" } */
+  free (buf2);	      /* { dg-warning "\\\[-Wfree-nonheap-object" } */
+  free (&c);	      /* { dg-warning "\\\[-Wfree-nonheap-object" } */
+  free (buf3);	      /* { dg-warning "\\\[-Wfree-nonheap-object" } */
+  free (&d);	      /* { dg-warning "\\\[-Wfree-nonheap-object" } */
+  free (buf4);	      /* { dg-warning "\\\[-Wfree-nonheap-object" } */
+  free (&e);	      /* { dg-warning "\\\[-Wfree-nonheap-object" } */
   free (&r->a);
-  free ("abcd");      /* { dg-warning "attempt to free a non-heap object" } */
-  free (L"abcd");     /* { dg-warning "attempt to free a non-heap object" } */
+  free ("abcd");      /* { dg-warning "\\\[-Wfree-nonheap-object" } */
+  free (L"abcd");     /* { dg-warning "\\\[-Wfree-nonheap-object" } */
 }
diff --git a/gcc/testsuite/gcc.dg/torture/pr71816.c b/gcc/testsuite/gcc.dg/torture/pr71816.c
index be37ad921bd..cc143fa6c7d 100644
--- a/gcc/testsuite/gcc.dg/torture/pr71816.c
+++ b/gcc/testsuite/gcc.dg/torture/pr71816.c
@@ -20,3 +20,7 @@ struct ext2_icount_el *insert_icount_el() {
     ext2fs_resize_mem(&insert_icount_el_icount_1);
     return 0;
 }
+
+/* Passing the address of a declared object to realloc triggers
+   -Wfree-nonheap-object unless -flto is used.
+   { dg-prune-output "\\\[-Wfree-nonheap-object" } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr19831-2.c b/gcc/testsuite/gcc.dg/tree-ssa/pr19831-2.c
index 55b4fd07580..df4120da582 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/pr19831-2.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr19831-2.c
@@ -5,7 +5,7 @@ void test1(void)
 {
   int *p = __builtin_malloc (sizeof (int) * 4);
   *p++ = 4;
-  __builtin_free (p);
+  __builtin_free (p);   // { dg-warning "\\\[-Wfree-nonheap-object" }
 }
 
 /* Undefined.  We can't do anything here.  */
diff --git a/libstdc++-v3/testsuite/ext/vstring/modifiers/clear/56166.cc b/libstdc++-v3/testsuite/ext/vstring/modifiers/clear/56166.cc
index 84dd79eef5e..b5ce78ddc7e 100644
--- a/libstdc++-v3/testsuite/ext/vstring/modifiers/clear/56166.cc
+++ b/libstdc++-v3/testsuite/ext/vstring/modifiers/clear/56166.cc
@@ -56,12 +56,12 @@ template<typename T>
           throw std::bad_alloc();
         }
       }
-      return (T*)new char[n * sizeof(T)];
+      return (T*)operator new (n * sizeof(T));
     }
 
     void deallocate(T* p, size_type)
     {
-      delete[] (char*)p;
+      operator delete (p);
     }
   };
 
@@ -94,3 +94,7 @@ int main()
     }
   }
 }
+
+// The __versa_string destructor triggers a bogus -Wfree-nonheap-object
+// due to pr54202.
+// { dg-prune-output "\\\[-Wfree-nonheap-object" }


More information about the Libstdc++-cvs mailing list