From 24592abd68e6efd4ab00c23d4702a046e63f24aa Mon Sep 17 00:00:00 2001 From: Marek Polacek Date: Tue, 1 Aug 2023 17:30:13 -0400 Subject: gcc: Introduce -fhardened In I proposed -fhardened, a new umbrella option that enables a reasonable set of hardening flags. The read of the room seems to be that the option would be useful. So here's a patch implementing that option. Currently, -fhardened enables: -D_FORTIFY_SOURCE=3 (or =2 for older glibcs) -D_GLIBCXX_ASSERTIONS -ftrivial-auto-var-init=zero -fPIE -pie -Wl,-z,relro,-z,now -fstack-protector-strong -fstack-clash-protection -fcf-protection=full (x86 GNU/Linux only) -fhardened will not override options that were specified on the command line (before or after -fhardened). For example, -D_FORTIFY_SOURCE=1 -fhardened means that _FORTIFY_SOURCE=1 will be used. Similarly, -fhardened -fstack-protector will not enable -fstack-protector-strong. Currently, -fhardened is only supported on GNU/Linux. In DW_AT_producer it is reflected only as -fhardened; it doesn't expand to anything. This patch provides -Whardened, enabled by default, which warns when -fhardened couldn't enable a particular option. I think most often it will say that _FORTIFY_SOURCE wasn't enabled because optimization were not enabled. gcc/c-family/ChangeLog: * c-opts.cc: Include "target.h". (c_finish_options): Maybe cpp_define _FORTIFY_SOURCE and _GLIBCXX_ASSERTIONS. gcc/ChangeLog: * common.opt (Whardened, fhardened): New options. * config.in: Regenerate. * config/bpf/bpf.cc: Include "opts.h". (bpf_option_override): If flag_stack_protector_set_by_fhardened_p, do not inform that -fstack-protector does not work. * config/i386/i386-options.cc (ix86_option_override_internal): When -fhardened, maybe enable -fcf-protection=full. * config/linux-protos.h (linux_fortify_source_default_level): Declare. * config/linux.cc (linux_fortify_source_default_level): New. * config/linux.h (TARGET_FORTIFY_SOURCE_DEFAULT_LEVEL): Redefine. * configure: Regenerate. * configure.ac: Check if the linker supports '-z now' and '-z relro'. Check if -fhardened is supported on $target_os. * doc/invoke.texi: Document -fhardened and -Whardened. * doc/tm.texi: Regenerate. * doc/tm.texi.in (TARGET_FORTIFY_SOURCE_DEFAULT_LEVEL): Add. * gcc.cc (driver_handle_option): Remember if any link options or -static were specified on the command line. (process_command): When -fhardened, maybe enable -pie and -Wl,-z,relro,-z,now. * opts.cc (flag_stack_protector_set_by_fhardened_p): New global. (finish_options): When -fhardened, enable -ftrivial-auto-var-init=zero and -fstack-protector-strong. (print_help_hardened): New. (print_help): Call it. * opts.h (flag_stack_protector_set_by_fhardened_p): Declare. * target.def (fortify_source_default_level): New target hook. * targhooks.cc (default_fortify_source_default_level): New. * targhooks.h (default_fortify_source_default_level): Declare. * toplev.cc (process_options): When -fhardened, enable -fstack-clash-protection. If flag_stack_protector_set_by_fhardened_p, do not warn that -fstack-protector not supported for this target. Don't enable -fhardened when !HAVE_FHARDENED_SUPPORT. gcc/testsuite/ChangeLog: * gcc.misc-tests/help.exp: Test -fhardened. * c-c++-common/fhardened-1.S: New test. * c-c++-common/fhardened-1.c: New test. * c-c++-common/fhardened-10.c: New test. * c-c++-common/fhardened-11.c: New test. * c-c++-common/fhardened-12.c: New test. * c-c++-common/fhardened-13.c: New test. * c-c++-common/fhardened-14.c: New test. * c-c++-common/fhardened-15.c: New test. * c-c++-common/fhardened-2.c: New test. * c-c++-common/fhardened-3.c: New test. * c-c++-common/fhardened-4.c: New test. * c-c++-common/fhardened-5.c: New test. * c-c++-common/fhardened-6.c: New test. * c-c++-common/fhardened-7.c: New test. * c-c++-common/fhardened-8.c: New test. * c-c++-common/fhardened-9.c: New test. * gcc.target/i386/cf_check-6.c: New test. --- gcc/gcc.cc | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) (limited to 'gcc/gcc.cc') diff --git a/gcc/gcc.cc b/gcc/gcc.cc index 51120c1..9f21ad9 100644 --- a/gcc/gcc.cc +++ b/gcc/gcc.cc @@ -302,6 +302,13 @@ static size_t dumpdir_length = 0; driver added to dumpdir after dumpbase or linker output name. */ static bool dumpdir_trailing_dash_added = false; +/* True if -r, -shared, -pie, or -no-pie were specified on the command + line. */ +static bool any_link_options_p; + +/* True if -static was specified on the command line. */ +static bool static_p; + /* Basename of dump and aux outputs, computed from dumpbase (given or derived from output name), to override input_basename in non-%w %b et al. */ @@ -4605,10 +4612,20 @@ driver_handle_option (struct gcc_options *opts, save_switch ("-o", 1, &arg, validated, true); return true; -#ifdef ENABLE_DEFAULT_PIE case OPT_pie: +#ifdef ENABLE_DEFAULT_PIE /* -pie is turned on by default. */ + validated = true; #endif + case OPT_r: + case OPT_shared: + case OPT_no_pie: + any_link_options_p = true; + break; + + case OPT_static: + static_p = true; + break; case OPT_static_libgcc: case OPT_shared_libgcc: @@ -4984,6 +5001,35 @@ process_command (unsigned int decoded_options_count, #endif } + /* TODO: check if -static -pie works and maybe use it. */ + if (flag_hardened) + { + if (!any_link_options_p && !static_p) + { +#ifdef HAVE_LD_PIE + save_switch (LD_PIE_SPEC, 0, NULL, /*validated=*/true, /*known=*/false); +#endif + /* These are passed straight down to collect2 so we have to break + it up like this. */ + if (HAVE_LD_NOW_SUPPORT) + { + add_infile ("-z", "*"); + add_infile ("now", "*"); + } + if (HAVE_LD_RELRO_SUPPORT) + { + add_infile ("-z", "*"); + add_infile ("relro", "*"); + } + } + /* We can't use OPT_Whardened yet. Sigh. */ + else if (warn_hardened) + warning_at (UNKNOWN_LOCATION, 0, + "linker hardening options not enabled by %<-fhardened%> " + "because other link options were specified on the command " + "line"); + } + /* Handle -gtoggle as it would later in toplev.cc:process_options to make the debug-level-gt spec function work as expected. */ if (flag_gtoggle) -- cgit v1.1