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"