diff mbox

warn for unsafe calls to __builtin_return_address

Message ID 557A0617.6040605@redhat.com
State New
Headers show

Commit Message

Martin Sebor June 11, 2015, 10:05 p.m. UTC
Attached is an updated patch for both GCC and the manual.

The patch implements the suggested warning, -Wbuiltin-address,
that issues diagnostics for unsafe calls of the builtin address
functions.  Safe calls are those with arguments 0 or 1 anywhere
in a program and argument 2 outside of the main function (since
every function has main as its direct or indirect caller).

Tested on powerpc64le and x86_64 Linux.

Martin

The ChangeLog entries for gcc and testsuite:

2015-06-11  Martin Sebor  <msebor@redhat.com>

	* c-family/c.opt (-Wbuiltin-address): New warning option.
	* doc/invoke.texi (Wbuiltin-address): Document it.
	* doc/extend.texi (__builtin_frame_addrress,
	__builtin_return_addrress):
	Clarify possible effects of calling the functions with
	non-zero arguments and mention -Wbuiltin-address.
	* builtins.c (expand_builtin_frame_address): Handle
	-Wbuiltin-address.

2015-06-11  Martin Sebor  <msebor@redhat.com>

	* g++.dg/Wbuiltin-address-in-Wall.C: New test.
	* g++.dg/Wbuiltin-address.C: New test.
	* g++.dg/Wno-builtin-address.C: New test.
	* gcc.dg/Wbuiltin-address-in-Wall.c: New test.
	* gcc.dg/Wbuiltin-address.c: New test.
	* gcc.dg/Wno-builtin-address.c: New test.

PS A few notes about the changes.

There's the following comment in expand_builtin_frame_address:

       /* Some ports cannot access arbitrary stack frames.  */

just before a block of code where the function can lead to
an "invalid argument" warning which would cause the newly
added tests to fail (since the newly added warning wouldn't
be issued).

I tried to determine what ports these might be so I could add
conditionals to the tests to prevent false positives there but
couldn't find any.

I wanted to also issue a warning for calls at file scope with
arguments greater than 1 (just like in main) but couldn't find
a way to determine that.

I also wanted to make the special treatment of main conditional
on whether or not -ffreestanding is in effect but flag_hosted
is not declared in builtins.c and bringing it into scope seemed
like too much of a change.

I'd be happy to modify the patch and add any of the above if
someone can suggest a way to do it without disrupting too much
code.

On 05/21/2015 03:39 PM, Pedro Alves wrote:
> On 05/21/2015 08:19 PM, Martin Sebor wrote:
>> A program I instrumented to help me debug an otherwise unrelated
>> problem in 5.1.0 has been crashing in calls to
>> __builtin_return_address. After checking the manual, I didn't
>> think I was doing anything wrong. I then did some debugging and
>> found that the function simply isn't safe to call with non-zero
>> arguments near the top of the stack. That seemed like a bug to
>> me so I created a small test case and ran it on a few targets
>> to see if the problem was isolated to just powerpc (where I'm
>> working at the moment) or more general. It turned out not to
>> be target-specific. Before opening a bug, I checked Bugzilla
>> to see if it's already been reported but couldn't find any open
>> reports. To be sure I wasn't missing something, I expanded my
>> search to already resolved bugs. That's when I finally found
>> pr8743 which had been closed years ago as a documentation issue,
>> after adding the following to the manual:
>>
>>     This function should only be used with a nonzero argument
>>     for debugging purposes.
>>
>> Since I was using the function exactly for this purpose, I'd
>> like to propose the patch below to clarify the effects of the
>> function to set the right expectations and help others avoid
>> the effort it took me to figure out this is by design.
>>
>> Does anyone have any concerns with this update or is it okay
>> to check in?
>
> Sounds like a good candidate for a warning.
>
> Thanks,
> Pedro Alves
>

Comments

Martin Sebor June 18, 2015, 5:15 p.m. UTC | #1
Are there any concerns with or suggestions for changes to
the following patch?

   https://gcc.gnu.org/ml/gcc-patches/2015-06/msg00886.html

Thanks
Martin
Martin Sebor June 26, 2015, 11:49 p.m. UTC | #2
Is this patch okay for trunk?

On 06/18/2015 11:15 AM, Martin Sebor wrote:
> Are there any concerns with or suggestions for changes to
> the following patch?
>
>    https://gcc.gnu.org/ml/gcc-patches/2015-06/msg00886.html
>
> Thanks
> Martin
Martin Sebor July 7, 2015, 3:41 a.m. UTC | #3
This is a small change to diagnose unsafe calls to
__builtin_{frame,return}_address (with an argument > 2) than
tend to return bogus values or lead to crashes at runtime.

A review would be appreciated.

Thanks
Martin

On 06/26/2015 05:49 PM, Martin Sebor wrote:
> Is this patch okay for trunk?
>
> On 06/18/2015 11:15 AM, Martin Sebor wrote:
>> Are there any concerns with or suggestions for changes to
>> the following patch?
>>
>>    https://gcc.gnu.org/ml/gcc-patches/2015-06/msg00886.html
>>
>> Thanks
>> Martin
>
Pedro Alves July 7, 2015, 10:14 a.m. UTC | #4
On 07/07/2015 04:41 AM, Martin Sebor wrote:
> This is a small change to diagnose unsafe calls to
> __builtin_{frame,return}_address (with an argument > 2) than
> tend to return bogus values or lead to crashes at runtime.
> 

I hadn't realized you went through and implemented the
suggestion.  Thanks for doing this.  I hope it gets approved.

> A review would be appreciated.

(It may be the relevant maintainers haven't noticed there's a
code patch here because this is nested within
the "[PATCH] clarify doc for __builtin_return_address" thread).)

Thanks,
Pedro Alves
Jeff Law July 24, 2015, 5:08 a.m. UTC | #5
On 06/11/2015 04:05 PM, Martin Sebor wrote:
> Attached is an updated patch for both GCC and the manual.
>
> The patch implements the suggested warning, -Wbuiltin-address,
> that issues diagnostics for unsafe calls of the builtin address
> functions.  Safe calls are those with arguments 0 or 1 anywhere
> in a program and argument 2 outside of the main function (since
> every function has main as its direct or indirect caller).
>
> Tested on powerpc64le and x86_64 Linux.
>
> Martin
>
> The ChangeLog entries for gcc and testsuite:
>
> 2015-06-11  Martin Sebor  <msebor@redhat.com>
>
>      * c-family/c.opt (-Wbuiltin-address): New warning option.
>      * doc/invoke.texi (Wbuiltin-address): Document it.
>      * doc/extend.texi (__builtin_frame_addrress,
>      __builtin_return_addrress):
>      Clarify possible effects of calling the functions with
>      non-zero arguments and mention -Wbuiltin-address.
>      * builtins.c (expand_builtin_frame_address): Handle
>      -Wbuiltin-address.
>
> 2015-06-11  Martin Sebor  <msebor@redhat.com>
>
>      * g++.dg/Wbuiltin-address-in-Wall.C: New test.
>      * g++.dg/Wbuiltin-address.C: New test.
>      * g++.dg/Wno-builtin-address.C: New test.
>      * gcc.dg/Wbuiltin-address-in-Wall.c: New test.
>      * gcc.dg/Wbuiltin-address.c: New test.
>      * gcc.dg/Wno-builtin-address.c: New test.
>
> PS A few notes about the changes.
>
> There's the following comment in expand_builtin_frame_address:
>
>        /* Some ports cannot access arbitrary stack frames.  */
>
> just before a block of code where the function can lead to
> an "invalid argument" warning which would cause the newly
> added tests to fail (since the newly added warning wouldn't
> be issued).
>
> I tried to determine what ports these might be so I could add
> conditionals to the tests to prevent false positives there but
> couldn't find any.
You have to start thinking creatively.  For example, consider ports 
without dwarf2 unwinders :-)  Consider ports where the linker inserts 
stubs between caller & callee for various reasons  Consider cases where 
the next outermost frame is compiled by something other than GCC, or 
perhaps was written in pure assembly.  Or consider the case when we're 
currently in a signal handling frame...






>
> I wanted to also issue a warning for calls at file scope with
> arguments greater than 1 (just like in main) but couldn't find
> a way to determine that.
>
> I also wanted to make the special treatment of main conditional
> on whether or not -ffreestanding is in effect but flag_hosted
> is not declared in builtins.c and bringing it into scope seemed
> like too much of a change.
>
> I'd be happy to modify the patch and add any of the above if
> someone can suggest a way to do it without disrupting too much
> code.
I don't think this is really an issue with calling with a non-zero 
argument near the top of the stack, but more an issue of the general 
difficulty in unwinding beyond the current frame.

It's relatively easy for the compiler to get the return address of the 
current frame -- after all, the compiler knows everything about the 
current function and can even arrange to avoid things which may make 
finding the current frame's address difficult.

However, once you want to go back one frame, you're in an arbitrary hunk 
of code and digging the prior frame out can be non-trivial.

You can get a sense of the complexities by looking at the GDB code to 
find the saved pc and base of an arbitrary frame.  It's insane, 
especially in a world without dwarf2 unwind records.

So, my suggestion would be to warn for any call with a nonzero value.

Jeff
Segher Boessenkool July 25, 2015, 2:20 p.m. UTC | #6
On Thu, Jul 23, 2015 at 11:08:21PM -0600, Jeff Law wrote:
> >There's the following comment in expand_builtin_frame_address:
> >
> >       /* Some ports cannot access arbitrary stack frames.  */
> >
> >just before a block of code where the function can lead to
> >an "invalid argument" warning which would cause the newly
> >added tests to fail (since the newly added warning wouldn't
> >be issued).
> >
> >I tried to determine what ports these might be so I could add
> >conditionals to the tests to prevent false positives there but
> >couldn't find any.

A quick grep shows aarch64.  Also many other ports, for
__builtin_return_address.  For __builtin_frame_address there is no
non-hacky way for ports to say "I cannot access other frames"; see
mmix_dynamic_chain_address for more complaints about this.

> You have to start thinking creatively.  For example, consider ports 
> without dwarf2 unwinders :-)  Consider ports where the linker inserts 
> stubs between caller & callee for various reasons  Consider cases where 
> the next outermost frame is compiled by something other than GCC, or 
> perhaps was written in pure assembly.  Or consider the case when we're 
> currently in a signal handling frame...

Or simply the case where the caller does not have a frame pointer, and
the ABI does not provide another way to get at (the size of) stack frames.

> So, my suggestion would be to warn for any call with a nonzero value.

The current documentation says that you should only use nonzero values
for debug purposes.  A warning would help yes, how many people read the
manual after all :-)


Segher
diff mbox

Patch

diff --git a/gcc/builtins.c b/gcc/builtins.c
index e8fe3db..58b0e13 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -4564,34 +4564,49 @@  expand_builtin_frame_address (tree fndecl, tree exp)
 {
   /* The argument must be a nonnegative integer constant.
      It counts the number of frames to scan up the stack.
-     The value is the return address saved in that frame.  */
+     The value is either the frame pointer value or the return
+     address saved in that frame.  */
   if (call_expr_nargs (exp) == 0)
     /* Warning about missing arg was already issued.  */
     return const0_rtx;
   else if (! tree_fits_uhwi_p (CALL_EXPR_ARG (exp, 0)))
     {
-      if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS)
-	error ("invalid argument to %<__builtin_frame_address%>");
-      else
-	error ("invalid argument to %<__builtin_return_address%>");
+      error ("invalid argument to %qD", fndecl);
       return const0_rtx;
     }
   else
     {
-      rtx tem
-	= expand_builtin_return_addr (DECL_FUNCTION_CODE (fndecl),
-				      tree_to_uhwi (CALL_EXPR_ARG (exp, 0)));
+      /* Number of frames to scan up the stack.  */
+      const unsigned HOST_WIDE_INT count = tree_to_uhwi (CALL_EXPR_ARG (exp, 0));
+
+      rtx tem = expand_builtin_return_addr (DECL_FUNCTION_CODE (fndecl), count);
 
       /* Some ports cannot access arbitrary stack frames.  */
       if (tem == NULL)
 	{
-	  if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS)
-	    warning (0, "unsupported argument to %<__builtin_frame_address%>");
-	  else
-	    warning (0, "unsupported argument to %<__builtin_return_address%>");
+	  warning (0, "invalid argument to %qD", fndecl);
 	  return const0_rtx;
 	}
 
+      if (1 < count)
+	{
+	  /* The number of frames the function can safely scan.
+	     Assume main has a caller, and (at least in a hosted
+	     implementation) every other function has main as its
+	     caller.  */
+	  unsigned safe_count = 2;
+
+	  if (DECL_NAME (current_function_decl)
+	      && MAIN_NAME_P (DECL_NAME (current_function_decl))
+	      && DECL_FILE_SCOPE_P (current_function_decl))
+	    safe_count = 1;
+
+	  if (safe_count < count)
+	    warning (OPT_Wbuiltin_address, "calling %qD with "
+		     "an argument greater than %u is unsafe here",
+		     fndecl, safe_count);
+	}
+
       /* For __builtin_frame_address, return what we've got.  */
       if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS)
 	return tem;
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 285952e..bdff624 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -295,6 +295,10 @@  Wbool-compare
 C ObjC C++ ObjC++ Var(warn_bool_compare) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
 Warn about boolean expression compared with an integer value different from true/false
 
+Wbuiltin-address
+C ObjC C++ ObjC++ Var(warn_builtin_address) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
+Warn when __builtin_frame_address or __builtin_return_address is used unsafely
+
 Wbuiltin-macro-redefined
 C ObjC C++ ObjC++ CPP(warn_builtin_macro_redefined) CppReason(CPP_W_BUILTIN_MACRO_REDEFINED) Var(cpp_warn_builtin_macro_redefined) Init(1) Warning
 Warn when a built-in preprocessor macro is undefined or redefined
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 9ad2b68..1b4596c 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -8562,8 +8562,11 @@  to determine if the top of the stack has been reached.
 Additional post-processing of the returned value may be needed, see
 @code{__builtin_extract_return_addr}.
 
-This function should only be used with a nonzero argument for debugging
-purposes.
+Calling this function with a nonzero argument can have unpredictable
+effects, including crashing the calling program.  As a result, calls
+that are considered unsafe are diagnosed when the @option{-Wbuiltin-address}
+option is in effect.  Such calls are typically only useful in debugging
+situations.
 @end deftypefn
 
 @deftypefn {Built-in Function} {void *} __builtin_extract_return_addr (void *@var{addr})
@@ -8601,8 +8604,11 @@  any function other than the current one; in such cases, or when the top
 of the stack has been reached, this function returns @code{0} if
 the first frame pointer is properly initialized by the startup code.
 
-This function should only be used with a nonzero argument for debugging
-purposes.
+Calling this function with a nonzero argument can have unpredictable
+effects, including crashing the calling program.  As a result, calls
+that are considered unsafe are diagnosed when the @option{-Wbuiltin-address}
+option is in effect.  Such calls are typically only useful in debugging
+situations.
 @end deftypefn
 
 @node Vector Extensions
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 5903c75..92887a8 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -241,7 +241,7 @@  Objective-C and Objective-C++ Dialects}.
 -pedantic-errors @gol
 -w  -Wextra  -Wall  -Waddress  -Waggregate-return  @gol
 -Waggressive-loop-optimizations -Warray-bounds -Warray-bounds=@var{n} @gol
--Wbool-compare @gol
+-Wbool-compare -Wbuiltin-address @gol
 -Wno-attributes -Wno-builtin-macro-redefined @gol
 -Wc90-c99-compat -Wc99-c11-compat @gol
 -Wc++-compat -Wc++11-compat -Wc++14-compat -Wcast-align  -Wcast-qual  @gol
@@ -4436,6 +4436,15 @@  if ((n > 1) == 2) @{ @dots{} @}
 @end smallexample
 This warning is enabled by @option{-Wall}.
 
+@item -Wbuiltin-address
+@opindex Wno-builtin-address
+@opindex Wbuiltin-address
+Warn when the @samp{__builtin_frame_address} or @samp{__builtin_return_address}
+is used unsafely.  Unsafe uses include calling the function with an argument
+greater than 1 in @samp{main} or greater than 2 anywhere else in a program.
+Such calls may return ndeterminate values or crash the program.  The warning
+is included in @option{-Wall}.
+
 @item -Wno-discarded-qualifiers @r{(C and Objective-C only)}
 @opindex Wno-discarded-qualifiers
 @opindex Wdiscarded-qualifiers
diff --git a/gcc/testsuite/g++.dg/Wbuiltin-address-in-Wall.C b/gcc/testsuite/g++.dg/Wbuiltin-address-in-Wall.C
new file mode 100644
index 0000000..aa9e17f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/Wbuiltin-address-in-Wall.C
@@ -0,0 +1,14 @@ 
+// { dg-do compile }
+// { dg-options "-Wall" }
+
+// Verify that -Wbuiltin-address is included in -Wall.
+
+void* test_builtin_address (unsigned i)
+{
+  void* const ba[] = {
+    __builtin_frame_address (4), // { dg-warning "builtin_frame_address" }
+    __builtin_return_address (4) // { dg-warning "builtin_return_address" }
+  };
+
+  return ba [i];
+}
diff --git a/gcc/testsuite/g++.dg/Wbuiltin-address.C b/gcc/testsuite/g++.dg/Wbuiltin-address.C
new file mode 100644
index 0000000..0be0244
--- /dev/null
+++ b/gcc/testsuite/g++.dg/Wbuiltin-address.C
@@ -0,0 +1,70 @@ 
+// { dg-do compile }
+// { dg-options "-Wbuiltin-address" }
+
+static void* const fa[] = {
+  __builtin_frame_address (0), // { dg-bogus "builtin_frame_address" }
+  __builtin_frame_address (1), // { dg-bogus "builtin_frame_address" }
+  __builtin_frame_address (2), // { dg-bogus "builtin_frame_address" }
+  __builtin_frame_address (3), // { dg-warning "builtin_frame_address" }
+  __builtin_frame_address (4)  // { dg-warning "builtin_frame_address" }
+};
+
+
+static void* const ra[] = {
+  __builtin_return_address (0), // { dg-bogus "builtin_return_address" }
+  __builtin_return_address (1), // { dg-bogus "builtin_return_address" }
+  __builtin_return_address (2), // { dg-bogus "builtin_return_address" }
+  __builtin_return_address (3), // { dg-warning "builtin_return_address" }
+  __builtin_return_address (4)  // { dg-warning "builtin_return_address" }
+};
+
+
+void* __attribute__ ((weak))
+test_builtin_frame_address (unsigned i)
+{
+  void* const fa[] = {
+    __builtin_frame_address (0), // { dg-bogus "builtin_frame_address" }
+    __builtin_frame_address (1), // { dg-bogus "builtin_frame_address" }
+    __builtin_frame_address (2), // { dg-bogus "builtin_frame_address" }
+    __builtin_frame_address (3), // { dg-warning "builtin_frame_address" }
+    __builtin_frame_address (4)  // { dg-warning "builtin_frame_address" }
+  };
+
+  return fa [i];
+}
+
+
+void* __attribute__ ((weak))
+test_builtin_return_address (unsigned i)
+{
+  void* const ra[] = {
+    __builtin_return_address (0), // { dg-bogus "builtin_return_address" }
+    __builtin_return_address (1), // { dg-bogus "builtin_return_address" }
+    __builtin_return_address (2), // { dg-bogus "builtin_return_address" }
+    __builtin_return_address (3), // { dg-warning "builtin_return_address" }
+    __builtin_return_address (4)  // { dg-warning "builtin_return_address" }
+  };
+  return ra [i];
+}
+
+
+int main ()
+{
+  test_builtin_frame_address (0);
+
+  test_builtin_return_address (0);
+
+  void* const a[] = {
+    __builtin_frame_address (0), // { dg-bogus "builtin_frame_address" }
+    __builtin_frame_address (1), // { dg-bogus "builtin_frame_address" }
+    __builtin_frame_address (2), // { dg-warning "builtin_frame_address" }
+    __builtin_frame_address (3), // { dg-warning "builtin_frame_address" }
+    __builtin_frame_address (4), // { dg-warning "builtin_frame_address" }
+
+    __builtin_return_address (0), // { dg-bogus "builtin_return_address" }
+    __builtin_return_address (1), // { dg-bogus "builtin_return_address" }
+    __builtin_return_address (2), // { dg-warning "builtin_return_address" }
+    __builtin_return_address (3), // { dg-warning "builtin_return_address" }
+    __builtin_return_address (4)  // { dg-warning "builtin_return_address" }
+  };
+}
diff --git a/gcc/testsuite/g++.dg/Wno-builtin-address.C b/gcc/testsuite/g++.dg/Wno-builtin-address.C
new file mode 100644
index 0000000..a460649
--- /dev/null
+++ b/gcc/testsuite/g++.dg/Wno-builtin-address.C
@@ -0,0 +1,6 @@ 
+// { dg-do compile }
+// { dg-options "-Werror" }
+
+// Verify that -Wbuiltin-address is not enabled by default by enabling
+// -Werror and verifying the test still compiles.
+#include "Wbuiltin-address.C"
diff --git a/gcc/testsuite/gcc.dg/Wbuiltin-address-in-Wall.c b/gcc/testsuite/gcc.dg/Wbuiltin-address-in-Wall.c
new file mode 100644
index 0000000..9f73810
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wbuiltin-address-in-Wall.c
@@ -0,0 +1,14 @@ 
+/* { dg-do compile } */
+/* { dg-options "-Wall" } */
+
+/* Verify that -Wbuiltin-address is included in -Wall.  */
+
+void* test_builtin_address (unsigned i)
+{
+  void* const ba[] = {
+    __builtin_frame_address (4), /* { dg-warning "builtin_frame_address" } */
+    __builtin_return_address (4)  /* { dg-warning "builtin_return_address" } */
+  };
+
+  return ba [i];
+}
diff --git a/gcc/testsuite/gcc.dg/Wbuiltin-address.c b/gcc/testsuite/gcc.dg/Wbuiltin-address.c
new file mode 100644
index 0000000..224ff30
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wbuiltin-address.c
@@ -0,0 +1,57 @@ 
+/* { dg-do compile } */
+/* { dg-options "-Wbuiltin-address" } */
+
+void* __attribute__ ((weak))
+test_builtin_frame_address (unsigned i)
+{
+  void* const fa[] = {
+    __builtin_frame_address (0), /* { dg-bogus "builtin_frame_address" } */
+    __builtin_frame_address (1), /* { dg-bogus "builtin_frame_address" } */
+    __builtin_frame_address (2), /* { dg-bogus "builtin_frame_address" } */
+    __builtin_frame_address (3), /* { dg-warning "builtin_frame_address" } */
+    __builtin_frame_address (4)  /* { dg-warning "builtin_frame_address" } */
+  };
+
+  return fa [i];
+}
+
+
+void* __attribute__ ((weak))
+test_builtin_return_address (unsigned i)
+{
+  void* const ra[] = {
+    __builtin_return_address (0), /* { dg-bogus "builtin_return_address" } */
+    __builtin_return_address (1), /* { dg-bogus "builtin_return_address" } */
+    __builtin_return_address (2), /* { dg-bogus "builtin_return_address" } */
+    __builtin_return_address (3), /* { dg-warning "builtin_return_address" } */
+    __builtin_return_address (4)  /* { dg-warning "builtin_return_address" } */
+  };
+  return ra [i];
+}
+
+
+int main (void)
+{
+  test_builtin_frame_address (0);
+
+  test_builtin_return_address (0);
+
+  // In main, calling the builtins is safe with an argument one less
+  // than in the rest of the program because all other callers have
+  // main as a (possibly indeirect) caller of their own.
+  void* const a[] = {
+    __builtin_frame_address (0), /* { dg-bogus "builtin_frame_address" } */
+    __builtin_frame_address (1), /* { dg-bogus "builtin_frame_address" } */
+    __builtin_frame_address (2), /* { dg-warning "builtin_frame_address" } */
+    __builtin_frame_address (3), /* { dg-warning "builtin_frame_address" } */
+    __builtin_frame_address (4), /* { dg-warning "builtin_frame_address" } */
+
+    __builtin_return_address (0), /* { dg-bogus "builtin_return_address" } */
+    __builtin_return_address (1), /* { dg-bogus "builtin_return_address" } */
+    __builtin_return_address (2), /* { dg-warning "builtin_return_address" } */
+    __builtin_return_address (3), /* { dg-warning "builtin_return_address" } */
+    __builtin_return_address (4)  /* { dg-warning "builtin_return_address" } */
+  };
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/Wno-builtin-address.c b/gcc/testsuite/gcc.dg/Wno-builtin-address.c
new file mode 100644
index 0000000..c2724d1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wno-builtin-address.c
@@ -0,0 +1,6 @@ 
+/* { dg-do compile } */
+/* { dg-options "-Werror" } */
+
+/* Verify that -Wbuiltin-address is not enabled by default by enabling
+   -Werror and verifying the test still compiles.  */
+#include "Wbuiltin-address.c"