@@ -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;
@@ -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
@@ -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
@@ -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
new file mode 100644
@@ -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];
+}
new file mode 100644
@@ -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" }
+ };
+}
new file mode 100644
@@ -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"
new file mode 100644
@@ -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];
+}
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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"