gcc: Introduce -fhardened

Message ID 20230915150816.18190-1-polacek@redhat.com
State Unresolved
Headers
Series gcc: Introduce -fhardened |

Checks

Context Check Description
snail/gcc-patch-check warning Git am fail log

Commit Message

Marek Polacek Sept. 15, 2023, 3:08 p.m. UTC
  Bootstrapped/regtested on x86_64-pc-linux-gnu, powerpc64le-unknown-linux-gnu,
and aarch64-unknown-linux-gnu; ok for trunk?

-- >8 --
In <https://gcc.gnu.org/pipermail/gcc-patches/2023-August/628748.html>
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=pattern
  -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.

In DW_AT_producer it is reflected only as -fhardened; it doesn't expand
to anything.  I think we need a better way to show what it actually
enables.

gcc/c-family/ChangeLog:

	* c-opts.cc (c_finish_options): Maybe cpp_define _FORTIFY_SOURCE
	and _GLIBCXX_ASSERTIONS.

gcc/ChangeLog:

	* common.opt (fhardened): New option.
	* 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.
	* configure: Regenerate.
	* configure.ac: Check if the linker supports '-z now' and '-z relro'.
	* doc/invoke.texi: Document -fhardened.
	* 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=pattern and -fstack-protector-strong.
	(print_help_hardened): New.
	(print_help): Call it.
	* 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.

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-2.c: New test.
	* c-c++-common/fhardened-3.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/c-family/c-opts.cc                     | 29 ++++++++++++
 gcc/common.opt                             |  4 ++
 gcc/config.in                              | 12 +++++
 gcc/config/bpf/bpf.cc                      |  8 ++--
 gcc/config/i386/i386-options.cc            | 11 ++++-
 gcc/configure                              | 50 +++++++++++++++++++-
 gcc/configure.ac                           | 42 ++++++++++++++++-
 gcc/doc/invoke.texi                        | 29 +++++++++++-
 gcc/gcc.cc                                 | 39 +++++++++++++++-
 gcc/opts.cc                                | 53 ++++++++++++++++++++--
 gcc/opts.h                                 |  1 +
 gcc/testsuite/c-c++-common/fhardened-1.S   |  6 +++
 gcc/testsuite/c-c++-common/fhardened-1.c   | 14 ++++++
 gcc/testsuite/c-c++-common/fhardened-10.c  | 10 ++++
 gcc/testsuite/c-c++-common/fhardened-11.c  | 10 ++++
 gcc/testsuite/c-c++-common/fhardened-12.c  | 11 +++++
 gcc/testsuite/c-c++-common/fhardened-13.c  |  6 +++
 gcc/testsuite/c-c++-common/fhardened-14.c  |  6 +++
 gcc/testsuite/c-c++-common/fhardened-2.c   |  9 ++++
 gcc/testsuite/c-c++-common/fhardened-3.c   | 12 +++++
 gcc/testsuite/c-c++-common/fhardened-5.c   | 11 +++++
 gcc/testsuite/c-c++-common/fhardened-6.c   | 11 +++++
 gcc/testsuite/c-c++-common/fhardened-7.c   |  7 +++
 gcc/testsuite/c-c++-common/fhardened-8.c   |  7 +++
 gcc/testsuite/c-c++-common/fhardened-9.c   |  6 +++
 gcc/testsuite/gcc.misc-tests/help.exp      |  2 +
 gcc/testsuite/gcc.target/i386/cf_check-6.c | 12 +++++
 gcc/toplev.cc                              | 11 ++++-
 28 files changed, 416 insertions(+), 13 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/fhardened-1.S
 create mode 100644 gcc/testsuite/c-c++-common/fhardened-1.c
 create mode 100644 gcc/testsuite/c-c++-common/fhardened-10.c
 create mode 100644 gcc/testsuite/c-c++-common/fhardened-11.c
 create mode 100644 gcc/testsuite/c-c++-common/fhardened-12.c
 create mode 100644 gcc/testsuite/c-c++-common/fhardened-13.c
 create mode 100644 gcc/testsuite/c-c++-common/fhardened-14.c
 create mode 100644 gcc/testsuite/c-c++-common/fhardened-2.c
 create mode 100644 gcc/testsuite/c-c++-common/fhardened-3.c
 create mode 100644 gcc/testsuite/c-c++-common/fhardened-5.c
 create mode 100644 gcc/testsuite/c-c++-common/fhardened-6.c
 create mode 100644 gcc/testsuite/c-c++-common/fhardened-7.c
 create mode 100644 gcc/testsuite/c-c++-common/fhardened-8.c
 create mode 100644 gcc/testsuite/c-c++-common/fhardened-9.c
 create mode 100644 gcc/testsuite/gcc.target/i386/cf_check-6.c


base-commit: 540a1d936d8f73f5e2efdefafd8342ec27773ae8
  

Comments

Richard Biener Sept. 18, 2023, 6:57 a.m. UTC | #1
On Fri, Sep 15, 2023 at 5:09 PM Marek Polacek via Gcc-patches
<gcc-patches@gcc.gnu.org> wrote:
>
> Bootstrapped/regtested on x86_64-pc-linux-gnu, powerpc64le-unknown-linux-gnu,
> and aarch64-unknown-linux-gnu; ok for trunk?
>
> -- >8 --
> In <https://gcc.gnu.org/pipermail/gcc-patches/2023-August/628748.html>
> 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=pattern
>   -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.
>
> In DW_AT_producer it is reflected only as -fhardened; it doesn't expand
> to anything.  I think we need a better way to show what it actually
> enables.

I do think we need to find a solution here to solve asserting compliance.
Maybe we can have -Whardened that will diagnose any altering of
-fhardened by other options on the command-line or by missed target
implementations?  People might for example use -fstack-protector
but don't really want to make protection lower than requested with -fhardened.

Any such conflict is much less appearant than when you use the
flags -fhardened composes.

Richard.

>
> gcc/c-family/ChangeLog:
>
>         * c-opts.cc (c_finish_options): Maybe cpp_define _FORTIFY_SOURCE
>         and _GLIBCXX_ASSERTIONS.
>
> gcc/ChangeLog:
>
>         * common.opt (fhardened): New option.
>         * 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.
>         * configure: Regenerate.
>         * configure.ac: Check if the linker supports '-z now' and '-z relro'.
>         * doc/invoke.texi: Document -fhardened.
>         * 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=pattern and -fstack-protector-strong.
>         (print_help_hardened): New.
>         (print_help): Call it.
>         * 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.
>
> 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-2.c: New test.
>         * c-c++-common/fhardened-3.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/c-family/c-opts.cc                     | 29 ++++++++++++
>  gcc/common.opt                             |  4 ++
>  gcc/config.in                              | 12 +++++
>  gcc/config/bpf/bpf.cc                      |  8 ++--
>  gcc/config/i386/i386-options.cc            | 11 ++++-
>  gcc/configure                              | 50 +++++++++++++++++++-
>  gcc/configure.ac                           | 42 ++++++++++++++++-
>  gcc/doc/invoke.texi                        | 29 +++++++++++-
>  gcc/gcc.cc                                 | 39 +++++++++++++++-
>  gcc/opts.cc                                | 53 ++++++++++++++++++++--
>  gcc/opts.h                                 |  1 +
>  gcc/testsuite/c-c++-common/fhardened-1.S   |  6 +++
>  gcc/testsuite/c-c++-common/fhardened-1.c   | 14 ++++++
>  gcc/testsuite/c-c++-common/fhardened-10.c  | 10 ++++
>  gcc/testsuite/c-c++-common/fhardened-11.c  | 10 ++++
>  gcc/testsuite/c-c++-common/fhardened-12.c  | 11 +++++
>  gcc/testsuite/c-c++-common/fhardened-13.c  |  6 +++
>  gcc/testsuite/c-c++-common/fhardened-14.c  |  6 +++
>  gcc/testsuite/c-c++-common/fhardened-2.c   |  9 ++++
>  gcc/testsuite/c-c++-common/fhardened-3.c   | 12 +++++
>  gcc/testsuite/c-c++-common/fhardened-5.c   | 11 +++++
>  gcc/testsuite/c-c++-common/fhardened-6.c   | 11 +++++
>  gcc/testsuite/c-c++-common/fhardened-7.c   |  7 +++
>  gcc/testsuite/c-c++-common/fhardened-8.c   |  7 +++
>  gcc/testsuite/c-c++-common/fhardened-9.c   |  6 +++
>  gcc/testsuite/gcc.misc-tests/help.exp      |  2 +
>  gcc/testsuite/gcc.target/i386/cf_check-6.c | 12 +++++
>  gcc/toplev.cc                              | 11 ++++-
>  28 files changed, 416 insertions(+), 13 deletions(-)
>  create mode 100644 gcc/testsuite/c-c++-common/fhardened-1.S
>  create mode 100644 gcc/testsuite/c-c++-common/fhardened-1.c
>  create mode 100644 gcc/testsuite/c-c++-common/fhardened-10.c
>  create mode 100644 gcc/testsuite/c-c++-common/fhardened-11.c
>  create mode 100644 gcc/testsuite/c-c++-common/fhardened-12.c
>  create mode 100644 gcc/testsuite/c-c++-common/fhardened-13.c
>  create mode 100644 gcc/testsuite/c-c++-common/fhardened-14.c
>  create mode 100644 gcc/testsuite/c-c++-common/fhardened-2.c
>  create mode 100644 gcc/testsuite/c-c++-common/fhardened-3.c
>  create mode 100644 gcc/testsuite/c-c++-common/fhardened-5.c
>  create mode 100644 gcc/testsuite/c-c++-common/fhardened-6.c
>  create mode 100644 gcc/testsuite/c-c++-common/fhardened-7.c
>  create mode 100644 gcc/testsuite/c-c++-common/fhardened-8.c
>  create mode 100644 gcc/testsuite/c-c++-common/fhardened-9.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/cf_check-6.c
>
> diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc
> index d9f55f45e03..004e37be9c0 100644
> --- a/gcc/c-family/c-opts.cc
> +++ b/gcc/c-family/c-opts.cc
> @@ -1514,6 +1514,9 @@ c_finish_options (void)
>        cb_file_change (parse_in, cmd_map);
>        linemap_line_start (line_table, 0, 1);
>
> +      bool fortify_seen_p = false;
> +      bool cxx_assert_seen_p = false;
> +
>        /* All command line defines must have the same location.  */
>        cpp_force_token_locations (parse_in, line_table->highest_line);
>        for (size_t i = 0; i < deferred_count; i++)
> @@ -1531,6 +1534,32 @@ c_finish_options (void)
>               else
>                 cpp_assert (parse_in, opt->arg);
>             }
> +
> +         if (UNLIKELY (flag_hardened)
> +             && (opt->code == OPT_D || opt->code == OPT_U))
> +           {
> +             if (!fortify_seen_p)
> +               fortify_seen_p
> +                 = (!strncmp (opt->arg, "_FORTIFY_SOURCE", 15)
> +                    && (opt->arg[15] == '\0' || opt->arg[15] == '='));
> +             if (!cxx_assert_seen_p)
> +               cxx_assert_seen_p
> +                 = (!strncmp (opt->arg, "_GLIBCXX_ASSERTIONS", 19)
> +                    && (opt->arg[19] == '\0' || opt->arg[19] == '='));
> +           }
> +       }
> +
> +      if (flag_hardened)
> +       {
> +         if (!fortify_seen_p && optimize > 0)
> +           {
> +             if (TARGET_GLIBC_MAJOR == 2 && TARGET_GLIBC_MINOR >= 35)
> +               cpp_define (parse_in, "_FORTIFY_SOURCE=3");
> +             else
> +               cpp_define (parse_in, "_FORTIFY_SOURCE=2");
> +           }
> +         if (!cxx_assert_seen_p)
> +           cpp_define (parse_in, "_GLIBCXX_ASSERTIONS");
>         }
>
>        cpp_stop_forcing_token_locations (parse_in);
> diff --git a/gcc/common.opt b/gcc/common.opt
> index f137a1f81ac..cc6238bd656 100644
> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -1827,6 +1827,10 @@ fharden-conditional-branches
>  Common Var(flag_harden_conditional_branches) Optimization
>  Harden conditional branches by checking reversed conditions.
>
> +fhardened
> +Common Driver Var(flag_hardened)
> +Enable various security-relevant flags.
> +
>  ; Nonzero means ignore `#ident' directives.  0 means handle them.
>  ; Generate position-independent code for executables if possible
>  ; On SVR4 targets, it also controls whether or not to emit a
> diff --git a/gcc/config.in b/gcc/config.in
> index f0071456f03..54c4999f36d 100644
> --- a/gcc/config.in
> +++ b/gcc/config.in
> @@ -1670,6 +1670,12 @@
>  #endif
>
>
> +/* Define 0/1 if your linker supports -z now */
> +#ifndef USED_FOR_TARGET
> +#undef HAVE_LD_NOW_SUPPORT
> +#endif
> +
> +
>  /* Define if your PowerPC64 linker only needs function descriptor syms. */
>  #ifndef USED_FOR_TARGET
>  #undef HAVE_LD_NO_DOT_SYMS
> @@ -1713,6 +1719,12 @@
>  #endif
>
>
> +/* Define 0/1 if your linker supports -z relro */
> +#ifndef USED_FOR_TARGET
> +#undef HAVE_LD_RELRO_SUPPORT
> +#endif
> +
> +
>  /* Define if your linker links a mix of read-only and read-write sections into
>     a read-write section. */
>  #ifndef USED_FOR_TARGET
> diff --git a/gcc/config/bpf/bpf.cc b/gcc/config/bpf/bpf.cc
> index 437bd652de3..41dc7fd3dae 100644
> --- a/gcc/config/bpf/bpf.cc
> +++ b/gcc/config/bpf/bpf.cc
> @@ -70,6 +70,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "gimplify-me.h"
>
>  #include "core-builtins.h"
> +#include "opts.h"
>
>  /* Per-function machine data.  */
>  struct GTY(()) machine_function
> @@ -250,9 +251,10 @@ bpf_option_override (void)
>    /* Disable -fstack-protector as it is not supported in BPF.  */
>    if (flag_stack_protect)
>      {
> -      inform (input_location,
> -              "%<-fstack-protector%> does not work "
> -             "on this architecture");
> +      if (!flag_stack_protector_set_by_fhardened_p)
> +       inform (input_location,
> +               "%<-fstack-protector%> does not work "
> +               "on this architecture");
>        flag_stack_protect = 0;
>      }
>
> diff --git a/gcc/config/i386/i386-options.cc b/gcc/config/i386/i386-options.cc
> index e47f9ed5d5f..963593bcd27 100644
> --- a/gcc/config/i386/i386-options.cc
> +++ b/gcc/config/i386/i386-options.cc
> @@ -3024,10 +3024,19 @@ ix86_option_override_internal (bool main_args_p,
>          = build_target_option_node (opts, opts_set);
>      }
>
> +  const bool cf_okay_p = (TARGET_64BIT || TARGET_CMOV);
> +  /* When -fhardened, enable -fcf-protection=full, but only when it's
> +     compatible with this target, and when it wasn't already specified
> +     on the command line.  */
> +  if (opts->x_flag_hardened
> +      && cf_okay_p
> +      && opts->x_flag_cf_protection == CF_NONE)
> +    opts->x_flag_cf_protection = CF_FULL;
> +
>    if (opts->x_flag_cf_protection != CF_NONE)
>      {
>        if ((opts->x_flag_cf_protection & CF_BRANCH) == CF_BRANCH
> -         && !TARGET_64BIT && !TARGET_CMOV)
> +         && !cf_okay_p)
>         error ("%<-fcf-protection%> is not compatible with this target");
>
>        opts->x_flag_cf_protection
> diff --git a/gcc/configure b/gcc/configure
> index 07e8a64afbb..9234a60636a 100755
> --- a/gcc/configure
> +++ b/gcc/configure
> @@ -32602,7 +32602,7 @@ if test x"$ld_is_gold" = xno; then
>        ld_bndplt_support=yes
>      fi
>    elif test x$gcc_cv_ld != x; then
> -    # Check if linker supports -a bndplt option
> +    # Check if linker supports -z bndplt option
>      if $gcc_cv_ld --help 2>&1 | grep -- '-z bndplt' > /dev/null; then
>        ld_bndplt_support=yes
>      fi
> @@ -32731,6 +32731,54 @@ $as_echo "#define ENABLE_S390_EXCESS_FLOAT_PRECISION 1" >>confdefs.h
>    ;;
>  esac
>
> +# Check if the linker supports '-z now'
> +ld_now_support=no
> +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking linker -z now option" >&5
> +$as_echo_n "checking linker -z now option... " >&6; }
> +if test x"$ld_is_gold" = xyes; then
> +  ld_now_support=yes
> +elif test $in_tree_ld = yes ; then
> +  if test "$gcc_cv_gld_major_version" -eq 2 -a "$gcc_cv_gld_minor_version" -ge 14 -o "$gcc_cv_gld_major_version" -gt 2; then
> +    ld_now_support=yes
> +  fi
> +elif test x$gcc_cv_ld != x; then
> +  # Check if linker supports -z now
> +  if $gcc_cv_ld --help 2>&1 | grep -- '-z now' > /dev/null; then
> +    ld_now_support=yes
> +  fi
> +fi
> +
> +cat >>confdefs.h <<_ACEOF
> +#define HAVE_LD_NOW_SUPPORT `if test x"$ld_now_support" = xyes; then echo 1; else echo 0; fi`
> +_ACEOF
> +
> +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_now_support" >&5
> +$as_echo "$ld_now_support" >&6; }
> +
> +# Check if the linker supports '-z relro'
> +ld_relro_support=no
> +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking linker -z relro option" >&5
> +$as_echo_n "checking linker -z relro option... " >&6; }
> +if test x"$ld_is_gold" = xyes; then
> +  ld_relro_support=yes
> +elif test $in_tree_ld = yes ; then
> +  if test "$gcc_cv_gld_major_version" -eq 2 -a "$gcc_cv_gld_minor_version" -ge 15 -o "$gcc_cv_gld_major_version" -gt 2; then
> +    ld_relro_support=yes
> +  fi
> +elif test x$gcc_cv_ld != x; then
> +  # Check if linker supports -z relro
> +  if $gcc_cv_ld --help 2>&1 | grep -- '-z relro' > /dev/null; then
> +    ld_relro_support=yes
> +  fi
> +fi
> +
> +cat >>confdefs.h <<_ACEOF
> +#define HAVE_LD_RELRO_SUPPORT `if test x"$ld_relro_support" = xyes; then echo 1; else echo 0; fi`
> +_ACEOF
> +
> +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_relro_support" >&5
> +$as_echo "$ld_relro_support" >&6; }
> +
>  # Configure the subdirectories
>  # AC_CONFIG_SUBDIRS($subdirs)
>
> diff --git a/gcc/configure.ac b/gcc/configure.ac
> index cb4be11facd..950a3f51370 100644
> --- a/gcc/configure.ac
> +++ b/gcc/configure.ac
> @@ -7623,7 +7623,7 @@ if test x"$ld_is_gold" = xno; then
>        ld_bndplt_support=yes
>      fi
>    elif test x$gcc_cv_ld != x; then
> -    # Check if linker supports -a bndplt option
> +    # Check if linker supports -z bndplt option
>      if $gcc_cv_ld --help 2>&1 | grep -- '-z bndplt' > /dev/null; then
>        ld_bndplt_support=yes
>      fi
> @@ -7724,6 +7724,46 @@ standards-compatible mode on s390 targets.])
>    ;;
>  esac
>
> +# Check if the linker supports '-z now'
> +ld_now_support=no
> +AC_MSG_CHECKING(linker -z now option)
> +if test x"$ld_is_gold" = xyes; then
> +  ld_now_support=yes
> +elif test $in_tree_ld = yes ; then
> +  if test "$gcc_cv_gld_major_version" -eq 2 -a "$gcc_cv_gld_minor_version" -ge 14 -o "$gcc_cv_gld_major_version" -gt 2; then
> +    ld_now_support=yes
> +  fi
> +elif test x$gcc_cv_ld != x; then
> +  # Check if linker supports -z now
> +  if $gcc_cv_ld --help 2>&1 | grep -- '-z now' > /dev/null; then
> +    ld_now_support=yes
> +  fi
> +fi
> +AC_DEFINE_UNQUOTED(HAVE_LD_NOW_SUPPORT,
> +  [`if test x"$ld_now_support" = xyes; then echo 1; else echo 0; fi`],
> +  [Define 0/1 if your linker supports -z now])
> +AC_MSG_RESULT($ld_now_support)
> +
> +# Check if the linker supports '-z relro'
> +ld_relro_support=no
> +AC_MSG_CHECKING(linker -z relro option)
> +if test x"$ld_is_gold" = xyes; then
> +  ld_relro_support=yes
> +elif test $in_tree_ld = yes ; then
> +  if test "$gcc_cv_gld_major_version" -eq 2 -a "$gcc_cv_gld_minor_version" -ge 15 -o "$gcc_cv_gld_major_version" -gt 2; then
> +    ld_relro_support=yes
> +  fi
> +elif test x$gcc_cv_ld != x; then
> +  # Check if linker supports -z relro
> +  if $gcc_cv_ld --help 2>&1 | grep -- '-z relro' > /dev/null; then
> +    ld_relro_support=yes
> +  fi
> +fi
> +AC_DEFINE_UNQUOTED(HAVE_LD_RELRO_SUPPORT,
> +  [`if test x"$ld_relro_support" = xyes; then echo 1; else echo 0; fi`],
> +  [Define 0/1 if your linker supports -z relro])
> +AC_MSG_RESULT($ld_relro_support)
> +
>  # Configure the subdirectories
>  # AC_CONFIG_SUBDIRS($subdirs)
>
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 03d93e6b185..ecd81c113ef 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -640,7 +640,7 @@ Objective-C and Objective-C++ Dialects}.
>  -fasan-shadow-offset=@var{number}  -fsanitize-sections=@var{s1},@var{s2},...
>  -fsanitize-undefined-trap-on-error  -fbounds-check
>  -fcf-protection=@r{[}full@r{|}branch@r{|}return@r{|}none@r{|}check@r{]}
> --fharden-compares -fharden-conditional-branches
> +-fharden-compares -fharden-conditional-branches -fhardened
>  -fstack-protector  -fstack-protector-all  -fstack-protector-strong
>  -fstack-protector-explicit  -fstack-check
>  -fstack-limit-register=@var{reg}  -fstack-limit-symbol=@var{sym}
> @@ -17369,6 +17369,33 @@ condition, and to call @code{__builtin_trap} if the result is
>  unexpected.  Use with @samp{-fharden-compares} to cover all
>  conditionals.
>
> +@opindex fhardened
> +@item -fhardened
> +Enable a set of flags for C and C++ that improve the security of the
> +generated code without affecting its ABI.  The precise flags enabled
> +may change between major releases of GCC, but are currently:
> +
> +@gccoptlist{
> +-D_FORTIFY_SOURCE=3
> +-D_GLIBCXX_ASSERTIONS
> +-ftrivial-auto-var-init=pattern
> +-fPIE  -pie  -Wl,-z,relro,-z,now
> +-fstack-protector-strong
> +-fstack-clash-protection
> +-fcf-protection=full @r{(x86 GNU/Linux only)}
> +}
> +
> +The list of options enabled by @option{-fhardened} can be generated using
> +the @option{--help=hardened} option.
> +
> +When the system glibc is older than 2.35, @option{-D_FORTIFY_SOURCE=2}
> +is used instead.
> +
> +@option{-fhardened} only enables a particular option if it wasn't
> +already specified anywhere on the command line.  For instance,
> +@option{-fhardened} @option{-fstack-protector} will only enable
> +@option{-fstack-protector}, but not @option{-fstack-protector-strong}.
> +
>  @opindex fstack-protector
>  @item -fstack-protector
>  Emit extra code to check for buffer overflows, such as stack smashing
> diff --git a/gcc/gcc.cc b/gcc/gcc.cc
> index a9dd0eb655c..db87c400c02 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.  */
> @@ -4597,10 +4604,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:
> @@ -4976,6 +4993,26 @@ process_command (unsigned int decoded_options_count,
>  #endif
>      }
>
> +  /* TODO: check if -static -pie works and maybe use it.  */
> +  if (flag_hardened && !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", "*");
> +       }
> +    }
> +
>    /* 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)
> diff --git a/gcc/opts.cc b/gcc/opts.cc
> index ac81d4e4294..03e1fdaf8d3 100644
> --- a/gcc/opts.cc
> +++ b/gcc/opts.cc
> @@ -43,6 +43,10 @@ along with GCC; see the file COPYING3.  If not see
>  /* Set by -fcanon-prefix-map.  */
>  bool flag_canon_prefix_map;
>
> +/* Set by finish_options when flag_stack_protector was set only because of
> +   -fhardened.  Yuck.  */
> +bool flag_stack_protector_set_by_fhardened_p;
> +
>  static void set_Wstrict_aliasing (struct gcc_options *opts, int onoff);
>
>  /* Names of fundamental debug info formats indexed by enum
> @@ -1093,6 +1097,9 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
>        opts->x_flag_section_anchors = 0;
>      }
>
> +  if (!opts_set->x_flag_auto_var_init && opts->x_flag_hardened)
> +    opts->x_flag_auto_var_init = AUTO_INIT_PATTERN;
> +
>    if (!opts->x_flag_opts_finished)
>      {
>        /* We initialize opts->x_flag_pie to -1 so that targets can set a
> @@ -1102,7 +1109,8 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
>           /* We initialize opts->x_flag_pic to -1 so that we can tell if
>              -fpic, -fPIC, -fno-pic or -fno-PIC is used.  */
>           if (opts->x_flag_pic == -1)
> -           opts->x_flag_pie = DEFAULT_FLAG_PIE;
> +           opts->x_flag_pie = (opts->x_flag_hardened
> +                               ? /*-fPIE*/ 2 : DEFAULT_FLAG_PIE);
>           else
>             opts->x_flag_pie = 0;
>         }
> @@ -1117,9 +1125,23 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
>      }
>
>    /* We initialize opts->x_flag_stack_protect to -1 so that targets
> -     can set a default value.  */
> +     can set a default value.  With --enable-default-ssp or -fhardened
> +     the default is -fstack-protector-strong.  */
>    if (opts->x_flag_stack_protect == -1)
> -    opts->x_flag_stack_protect = DEFAULT_FLAG_SSP;
> +    {
> +      /* This should check FRAME_GROWS_DOWNWARD, but on some targets it's
> +        defined in such a way that it uses flag_stack_protect which can't
> +        be used here.  Moreover, some targets like BPF don't support
> +        -fstack-protector at all but we don't know that here.  So remember
> +        that flag_stack_protect was set at the behest of -fhardened.  */
> +      if (opts->x_flag_hardened)
> +       {
> +         opts->x_flag_stack_protect = SPCT_FLAG_STRONG;
> +         flag_stack_protector_set_by_fhardened_p = true;
> +       }
> +      else
> +       opts->x_flag_stack_protect = DEFAULT_FLAG_SSP;
> +    }
>
>    if (opts->x_optimize == 0)
>      {
> @@ -2461,6 +2483,29 @@ parse_and_check_patch_area (const char *arg, bool report_error,
>    free (patch_area_arg);
>  }
>
> +/* Print options enabled by -fhardened.  */
> +
> +static void
> +print_help_hardened ()
> +{
> +  printf ("%s\n", "The following options are enabled by -fhardened:");
> +  printf ("  %s=%d\n", "-D_FORTIFY_SOURCE",
> +         (TARGET_GLIBC_MAJOR == 2 && TARGET_GLIBC_MINOR >= 35) ? 3 : 2);
> +  printf ("  %s\n", "-D_GLIBCXX_ASSERTIONS");
> +  printf ("  %s\n", "-ftrivial-auto-var-init=pattern");
> +#ifdef HAVE_LD_PIE
> +  printf ("  %s  %s\n", "-fPIE", "-pie");
> +#endif
> +  if (HAVE_LD_NOW_SUPPORT)
> +    printf ("  %s\n", "-Wl,-z,now");
> +  if (HAVE_LD_RELRO_SUPPORT)
> +    printf ("  %s\n", "-Wl,-z,relro");
> +  printf ("  %s\n", "-fstack-protector-strong");
> +  printf ("  %s\n", "-fstack-clash-protection");
> +  printf ("  %s\n", "-fcf-protection=full");
> +  putchar ('\n');
> +}
> +
>  /* Print help when OPT__help_ is set.  */
>
>  void
> @@ -2576,6 +2621,8 @@ print_help (struct gcc_options *opts, unsigned int lang_mask,
>         }
>        else if (lang_flag != 0)
>         *pflags |= lang_flag;
> +      else if (strncasecmp (a, "hardened", len) == 0)
> +       print_help_hardened ();
>        else
>         warning (0,
>                  "unrecognized argument to %<--help=%> option: %q.*s",
> diff --git a/gcc/opts.h b/gcc/opts.h
> index 00f377f9ca7..d89c5de8114 100644
> --- a/gcc/opts.h
> +++ b/gcc/opts.h
> @@ -344,6 +344,7 @@ struct cl_option_handlers
>  /* Hold command-line options associated with stack limitation.  */
>  extern const char *opt_fstack_limit_symbol_arg;
>  extern int opt_fstack_limit_register_no;
> +extern bool flag_stack_protector_set_by_fhardened_p;
>
>  /* Input file names.  */
>
> diff --git a/gcc/testsuite/c-c++-common/fhardened-1.S b/gcc/testsuite/c-c++-common/fhardened-1.S
> new file mode 100644
> index 00000000000..026c7afbaad
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/fhardened-1.S
> @@ -0,0 +1,6 @@
> +/* { dg-do preprocess { target pie } } */
> +/* { dg-options "-fhardened" } */
> +
> +#if __PIE__ != 2
> +# error "-fPIE not enabled"
> +#endif
> diff --git a/gcc/testsuite/c-c++-common/fhardened-1.c b/gcc/testsuite/c-c++-common/fhardened-1.c
> new file mode 100644
> index 00000000000..dd3cde93805
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/fhardened-1.c
> @@ -0,0 +1,14 @@
> +/* { dg-do compile } */
> +/* { dg-options "-fhardened -O" } */
> +
> +#ifndef __SSP_STRONG__
> +# error "-fstack-protector-strong not enabled"
> +#endif
> +
> +#if _FORTIFY_SOURCE < 2
> +# error "_FORTIFY_SOURCE not enabled"
> +#endif
> +
> +#ifndef _GLIBCXX_ASSERTIONS
> +# error "_GLIBCXX_ASSERTIONS not enabled"
> +#endif
> diff --git a/gcc/testsuite/c-c++-common/fhardened-10.c b/gcc/testsuite/c-c++-common/fhardened-10.c
> new file mode 100644
> index 00000000000..431bd5d3a80
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/fhardened-10.c
> @@ -0,0 +1,10 @@
> +/* { dg-do compile } */
> +/* { dg-options "-fhardened -D_FORTIFY_SOURCE=1" } */
> +
> +#if _FORTIFY_SOURCE != 1
> +# error "_FORTIFY_SOURCE != 1"
> +#endif
> +
> +#ifndef _GLIBCXX_ASSERTIONS
> +# error "_GLIBCXX_ASSERTIONS disabled when it should not be"
> +#endif
> diff --git a/gcc/testsuite/c-c++-common/fhardened-11.c b/gcc/testsuite/c-c++-common/fhardened-11.c
> new file mode 100644
> index 00000000000..b8fd65a960c
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/fhardened-11.c
> @@ -0,0 +1,10 @@
> +/* { dg-do compile } */
> +/* { dg-options "-fhardened -O -D_FORTIFY_SOURCE_ -D_GLIBCXX_ASSERTIONS_" } */
> +
> +#ifndef _FORTIFY_SOURCE
> +# error "_FORTIFY_SOURCE disabled when it should not be"
> +#endif
> +
> +#ifndef _GLIBCXX_ASSERTIONS
> +# error "_GLIBCXX_ASSERTIONS disabled when it should not be"
> +#endif
> diff --git a/gcc/testsuite/c-c++-common/fhardened-12.c b/gcc/testsuite/c-c++-common/fhardened-12.c
> new file mode 100644
> index 00000000000..340a7bdad6d
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/fhardened-12.c
> @@ -0,0 +1,11 @@
> +/* { dg-do compile } */
> +/* { dg-options "-fhardened -fdump-tree-gimple" } */
> +
> +int
> +foo ()
> +{
> +  int i;
> +  return i;
> +}
> +
> +/* { dg-final { scan-tree-dump ".DEFERRED_INIT" "gimple" } } */
> diff --git a/gcc/testsuite/c-c++-common/fhardened-13.c b/gcc/testsuite/c-c++-common/fhardened-13.c
> new file mode 100644
> index 00000000000..629ff6ff94b
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/fhardened-13.c
> @@ -0,0 +1,6 @@
> +/* { dg-do compile { target pie } } */
> +/* { dg-options "-fhardened" } */
> +
> +#if __PIE__ != 2
> +# error "-fPIE not enabled"
> +#endif
> diff --git a/gcc/testsuite/c-c++-common/fhardened-14.c b/gcc/testsuite/c-c++-common/fhardened-14.c
> new file mode 100644
> index 00000000000..bfcd000fe6a
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/fhardened-14.c
> @@ -0,0 +1,6 @@
> +/* { dg-do compile { target pie } } */
> +/* { dg-options "-fhardened -fno-PIE" } */
> +
> +#ifdef __PIE__
> +# error "PIE enabled when it should not be"
> +#endif
> diff --git a/gcc/testsuite/c-c++-common/fhardened-2.c b/gcc/testsuite/c-c++-common/fhardened-2.c
> new file mode 100644
> index 00000000000..aeba4e40a42
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/fhardened-2.c
> @@ -0,0 +1,9 @@
> +/* { dg-do compile } */
> +/* { dg-options "-fhardened -fstack-protector" } */
> +
> +#ifdef __SSP_STRONG__
> +# error "-fstack-protector-strong enabled when it should not be"
> +#endif
> +#ifndef __SSP__
> +# error "-fstack-protector not enabled"
> +#endif
> diff --git a/gcc/testsuite/c-c++-common/fhardened-3.c b/gcc/testsuite/c-c++-common/fhardened-3.c
> new file mode 100644
> index 00000000000..105e013d734
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/fhardened-3.c
> @@ -0,0 +1,12 @@
> +/* { dg-do compile } */
> +/* { dg-options "-fhardened -O0" } */
> +/* Test that we don't get any diagnostic coming from libc headers.  */
> +
> +#include <stdio.h>
> +
> +/* The most useful C program known to man.  */
> +
> +int
> +main ()
> +{
> +}
> diff --git a/gcc/testsuite/c-c++-common/fhardened-5.c b/gcc/testsuite/c-c++-common/fhardened-5.c
> new file mode 100644
> index 00000000000..340a7bdad6d
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/fhardened-5.c
> @@ -0,0 +1,11 @@
> +/* { dg-do compile } */
> +/* { dg-options "-fhardened -fdump-tree-gimple" } */
> +
> +int
> +foo ()
> +{
> +  int i;
> +  return i;
> +}
> +
> +/* { dg-final { scan-tree-dump ".DEFERRED_INIT" "gimple" } } */
> diff --git a/gcc/testsuite/c-c++-common/fhardened-6.c b/gcc/testsuite/c-c++-common/fhardened-6.c
> new file mode 100644
> index 00000000000..478caf9895b
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/fhardened-6.c
> @@ -0,0 +1,11 @@
> +/* { dg-do compile } */
> +/* { dg-options "-fhardened -ftrivial-auto-var-init=uninitialized -fdump-tree-gimple" } */
> +
> +int
> +foo ()
> +{
> +  int i;
> +  return i;
> +}
> +
> +/* { dg-final { scan-tree-dump-not ".DEFERRED_INIT" "gimple" } } */
> diff --git a/gcc/testsuite/c-c++-common/fhardened-7.c b/gcc/testsuite/c-c++-common/fhardened-7.c
> new file mode 100644
> index 00000000000..daf91f2ca25
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/fhardened-7.c
> @@ -0,0 +1,7 @@
> +/* { dg-do compile { target pie } } */
> +/* { dg-options "-fhardened -fpie" } */
> +
> +/* -fpie takes precedence over -fhardened */
> +#if __PIE__ != 1
> +# error "__PIE__ != 1"
> +#endif
> diff --git a/gcc/testsuite/c-c++-common/fhardened-8.c b/gcc/testsuite/c-c++-common/fhardened-8.c
> new file mode 100644
> index 00000000000..87e2750efe2
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/fhardened-8.c
> @@ -0,0 +1,7 @@
> +/* { dg-do compile { target pie } } */
> +/* { dg-options "-fhardened -fPIC" } */
> +
> +/* -fPIC takes precedence over -fhardened */
> +#ifdef __PIE__
> +# error "PIE enabled when it should not be"
> +#endif
> diff --git a/gcc/testsuite/c-c++-common/fhardened-9.c b/gcc/testsuite/c-c++-common/fhardened-9.c
> new file mode 100644
> index 00000000000..f64e7eba56c
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/fhardened-9.c
> @@ -0,0 +1,6 @@
> +/* { dg-do compile } */
> +/* { dg-options "-fhardened -U_FORTIFY_SOURCE -U_GLIBCXX_ASSERTIONS" } */
> +
> +#if defined(_FORTIFY_SOURCE) || defined(_GLIBCXX_ASSERTIONS)
> +# error "hardening enabled when it should not be"
> +#endif
> diff --git a/gcc/testsuite/gcc.misc-tests/help.exp b/gcc/testsuite/gcc.misc-tests/help.exp
> index 52b9cb0ab90..15d618a2528 100644
> --- a/gcc/testsuite/gcc.misc-tests/help.exp
> +++ b/gcc/testsuite/gcc.misc-tests/help.exp
> @@ -151,6 +151,8 @@ foreach cls { "ada" "c" "c++" "d" "fortran" "go" \
>  # Listing only excludes gives empty results.
>  check_for_options c "--help=^joined,^separate" "" "" ""
>
> +check_for_options c "--help=hardened" "The following options are enabled by -fhardened" "" ""
> +
>  if [ info exists prev_columns ] {
>      # Reset the enviroment variable to its oriuginal value.
>      set env(COLUMNS) $prev_columns
> diff --git a/gcc/testsuite/gcc.target/i386/cf_check-6.c b/gcc/testsuite/gcc.target/i386/cf_check-6.c
> new file mode 100644
> index 00000000000..73b78dce889
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/cf_check-6.c
> @@ -0,0 +1,12 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fhardened -mno-manual-endbr" } */
> +/* { dg-final { scan-assembler-times {\mendbr} 1 } } */
> +/* Test that -fhardened enables CET.  */
> +
> +extern void bar (void) __attribute__((__cf_check__));
> +
> +void
> +foo (void)
> +{
> +  bar ();
> +}
> diff --git a/gcc/toplev.cc b/gcc/toplev.cc
> index db62e3e995e..68d744630e1 100644
> --- a/gcc/toplev.cc
> +++ b/gcc/toplev.cc
> @@ -1575,6 +1575,12 @@ process_options (bool no_backend)
>                   "where the stack grows from lower to higher addresses");
>        flag_stack_clash_protection = 0;
>      }
> +  else if (flag_hardened
> +          && !flag_stack_clash_protection
> +          /* Don't enable -fstack-clash-protection when -fstack-check=
> +             is used: it would result in confusing errors.  */
> +          && flag_stack_check == NO_STACK_CHECK)
> +    flag_stack_clash_protection = 1;
>
>    /* We cannot support -fstack-check= and -fstack-clash-protection at
>       the same time.  */
> @@ -1590,8 +1596,9 @@ process_options (bool no_backend)
>       target already uses a soft frame pointer, the transition is trivial.  */
>    if (!FRAME_GROWS_DOWNWARD && flag_stack_protect)
>      {
> -      warning_at (UNKNOWN_LOCATION, 0,
> -                 "%<-fstack-protector%> not supported for this target");
> +      if (!flag_stack_protector_set_by_fhardened_p)
> +       warning_at (UNKNOWN_LOCATION, 0,
> +                   "%<-fstack-protector%> not supported for this target");
>        flag_stack_protect = 0;
>      }
>    if (!flag_stack_protect)
>
> base-commit: 540a1d936d8f73f5e2efdefafd8342ec27773ae8
> --
> 2.41.0
>
  
Marek Polacek Sept. 19, 2023, 2:58 p.m. UTC | #2
On Mon, Sep 18, 2023 at 08:57:39AM +0200, Richard Biener wrote:
> On Fri, Sep 15, 2023 at 5:09 PM Marek Polacek via Gcc-patches
> <gcc-patches@gcc.gnu.org> wrote:
> >
> > Bootstrapped/regtested on x86_64-pc-linux-gnu, powerpc64le-unknown-linux-gnu,
> > and aarch64-unknown-linux-gnu; ok for trunk?
> >
> > -- >8 --
> > In <https://gcc.gnu.org/pipermail/gcc-patches/2023-August/628748.html>
> > 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=pattern
> >   -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.
> >
> > In DW_AT_producer it is reflected only as -fhardened; it doesn't expand
> > to anything.  I think we need a better way to show what it actually
> > enables.
> 
> I do think we need to find a solution here to solve asserting compliance.

Fair enough.

> Maybe we can have -Whardened that will diagnose any altering of
> -fhardened by other options on the command-line or by missed target
> implementations?  People might for example use -fstack-protector
> but don't really want to make protection lower than requested with -fhardened.
> 
> Any such conflict is much less appearant than when you use the
> flags -fhardened composes.

How about: --help=hardened says which options -fhardened attempts to
enable, and -Whardened warns when it didn't enable an option?  E.g.,

  -fstack-protector -fhardened -Whardened

would say that it didn't enable -fstack-protector-strong because
-fstack-protector was specified on the command line?

If !HAVE_LD_NOW_SUPPORT, --help=hardened probably doesn't even have to
list -z now, likewise for -z relro.

Unclear if -Whardened should be enabled by default, but probably yes?

Marek
  
Jakub Jelinek Sept. 19, 2023, 3:14 p.m. UTC | #3
On Tue, Sep 19, 2023 at 10:58:19AM -0400, Marek Polacek wrote:
> > > In <https://gcc.gnu.org/pipermail/gcc-patches/2023-August/628748.html>
> > > 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=pattern
> > >   -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.
> > >
> > > In DW_AT_producer it is reflected only as -fhardened; it doesn't expand
> > > to anything.  I think we need a better way to show what it actually
> > > enables.
> > 
> > I do think we need to find a solution here to solve asserting compliance.
> 
> Fair enough.

Well, asserting compliance doesn't make sense, because many of these features
are only best effort.  So, one can assert that certain options have been
passed to the compiler (or not), and for that the current
-grecord-gcc-switches I think works mostly fine (well, it doesn't record -D*
options), one knows the compiler version/snapshot date etc. and what options
have been passed and can by repeating those options see what is and isn't
enabled in that case.
As for what exact options are actually enabled in the end, --help should be
able to answer that.  Still, even if one records that -D_FORTIFY_SOURCE=3
was passed on the command line, that doesn't mean there is no
#undef _FORTIFY_SOURCE in the source before including headers, or that
the compiler has been successful to figure out object size (static or
dynamic) for certain pointer, or that a function has some array so that it
will use stack protector guard, or that certain function didn't disable
-fstack-protector through function attributes etc.
So, if one wants to know if certain vulnerability exploit can be stopped
through hardening, one needs to analyze actually emitted code, just looking
for checkboxes isn't enough.
One can assert -D_FORTIFY_SOURCE=2 has been passed, but without glibc
headers being used and actually using __builtin_*object_size it doesn't do
anything either.  Programs can just declare memcpy etc. themselves and not
include glibc headers...

If the checkboxes are desirable for some reason, perhaps we could introduce
a new DWARF DW_AT_GNU_hardening attribute which would contain say bitfield
of which of those hardening features have been enabled (though, not sure if
we want to emit them just per DW_TAG_compile_unit, or DW_TAG_subprogram,
or both (say put on DW_TAG_compile_unit always if at least one of those is
enabled and on DW_TAG_subprogram only if it is different from the CU one).

	Jakub
  

Patch

diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc
index d9f55f45e03..004e37be9c0 100644
--- a/gcc/c-family/c-opts.cc
+++ b/gcc/c-family/c-opts.cc
@@ -1514,6 +1514,9 @@  c_finish_options (void)
       cb_file_change (parse_in, cmd_map);
       linemap_line_start (line_table, 0, 1);
 
+      bool fortify_seen_p = false;
+      bool cxx_assert_seen_p = false;
+
       /* All command line defines must have the same location.  */
       cpp_force_token_locations (parse_in, line_table->highest_line);
       for (size_t i = 0; i < deferred_count; i++)
@@ -1531,6 +1534,32 @@  c_finish_options (void)
 	      else
 		cpp_assert (parse_in, opt->arg);
 	    }
+
+	  if (UNLIKELY (flag_hardened)
+	      && (opt->code == OPT_D || opt->code == OPT_U))
+	    {
+	      if (!fortify_seen_p)
+		fortify_seen_p
+		  = (!strncmp (opt->arg, "_FORTIFY_SOURCE", 15)
+		     && (opt->arg[15] == '\0' || opt->arg[15] == '='));
+	      if (!cxx_assert_seen_p)
+		cxx_assert_seen_p
+		  = (!strncmp (opt->arg, "_GLIBCXX_ASSERTIONS", 19)
+		     && (opt->arg[19] == '\0' || opt->arg[19] == '='));
+	    }
+	}
+
+      if (flag_hardened)
+	{
+	  if (!fortify_seen_p && optimize > 0)
+	    {
+	      if (TARGET_GLIBC_MAJOR == 2 && TARGET_GLIBC_MINOR >= 35)
+		cpp_define (parse_in, "_FORTIFY_SOURCE=3");
+	      else
+		cpp_define (parse_in, "_FORTIFY_SOURCE=2");
+	    }
+	  if (!cxx_assert_seen_p)
+	    cpp_define (parse_in, "_GLIBCXX_ASSERTIONS");
 	}
 
       cpp_stop_forcing_token_locations (parse_in);
diff --git a/gcc/common.opt b/gcc/common.opt
index f137a1f81ac..cc6238bd656 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1827,6 +1827,10 @@  fharden-conditional-branches
 Common Var(flag_harden_conditional_branches) Optimization
 Harden conditional branches by checking reversed conditions.
 
+fhardened
+Common Driver Var(flag_hardened)
+Enable various security-relevant flags.
+
 ; Nonzero means ignore `#ident' directives.  0 means handle them.
 ; Generate position-independent code for executables if possible
 ; On SVR4 targets, it also controls whether or not to emit a
diff --git a/gcc/config.in b/gcc/config.in
index f0071456f03..54c4999f36d 100644
--- a/gcc/config.in
+++ b/gcc/config.in
@@ -1670,6 +1670,12 @@ 
 #endif
 
 
+/* Define 0/1 if your linker supports -z now */
+#ifndef USED_FOR_TARGET
+#undef HAVE_LD_NOW_SUPPORT
+#endif
+
+
 /* Define if your PowerPC64 linker only needs function descriptor syms. */
 #ifndef USED_FOR_TARGET
 #undef HAVE_LD_NO_DOT_SYMS
@@ -1713,6 +1719,12 @@ 
 #endif
 
 
+/* Define 0/1 if your linker supports -z relro */
+#ifndef USED_FOR_TARGET
+#undef HAVE_LD_RELRO_SUPPORT
+#endif
+
+
 /* Define if your linker links a mix of read-only and read-write sections into
    a read-write section. */
 #ifndef USED_FOR_TARGET
diff --git a/gcc/config/bpf/bpf.cc b/gcc/config/bpf/bpf.cc
index 437bd652de3..41dc7fd3dae 100644
--- a/gcc/config/bpf/bpf.cc
+++ b/gcc/config/bpf/bpf.cc
@@ -70,6 +70,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "gimplify-me.h"
 
 #include "core-builtins.h"
+#include "opts.h"
 
 /* Per-function machine data.  */
 struct GTY(()) machine_function
@@ -250,9 +251,10 @@  bpf_option_override (void)
   /* Disable -fstack-protector as it is not supported in BPF.  */
   if (flag_stack_protect)
     {
-      inform (input_location,
-              "%<-fstack-protector%> does not work "
-	      "on this architecture");
+      if (!flag_stack_protector_set_by_fhardened_p)
+	inform (input_location,
+		"%<-fstack-protector%> does not work "
+		"on this architecture");
       flag_stack_protect = 0;
     }
 
diff --git a/gcc/config/i386/i386-options.cc b/gcc/config/i386/i386-options.cc
index e47f9ed5d5f..963593bcd27 100644
--- a/gcc/config/i386/i386-options.cc
+++ b/gcc/config/i386/i386-options.cc
@@ -3024,10 +3024,19 @@  ix86_option_override_internal (bool main_args_p,
         = build_target_option_node (opts, opts_set);
     }
 
+  const bool cf_okay_p = (TARGET_64BIT || TARGET_CMOV);
+  /* When -fhardened, enable -fcf-protection=full, but only when it's
+     compatible with this target, and when it wasn't already specified
+     on the command line.  */
+  if (opts->x_flag_hardened
+      && cf_okay_p
+      && opts->x_flag_cf_protection == CF_NONE)
+    opts->x_flag_cf_protection = CF_FULL;
+
   if (opts->x_flag_cf_protection != CF_NONE)
     {
       if ((opts->x_flag_cf_protection & CF_BRANCH) == CF_BRANCH
-	  && !TARGET_64BIT && !TARGET_CMOV)
+	  && !cf_okay_p)
 	error ("%<-fcf-protection%> is not compatible with this target");
 
       opts->x_flag_cf_protection
diff --git a/gcc/configure b/gcc/configure
index 07e8a64afbb..9234a60636a 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -32602,7 +32602,7 @@  if test x"$ld_is_gold" = xno; then
       ld_bndplt_support=yes
     fi
   elif test x$gcc_cv_ld != x; then
-    # Check if linker supports -a bndplt option
+    # Check if linker supports -z bndplt option
     if $gcc_cv_ld --help 2>&1 | grep -- '-z bndplt' > /dev/null; then
       ld_bndplt_support=yes
     fi
@@ -32731,6 +32731,54 @@  $as_echo "#define ENABLE_S390_EXCESS_FLOAT_PRECISION 1" >>confdefs.h
   ;;
 esac
 
+# Check if the linker supports '-z now'
+ld_now_support=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking linker -z now option" >&5
+$as_echo_n "checking linker -z now option... " >&6; }
+if test x"$ld_is_gold" = xyes; then
+  ld_now_support=yes
+elif test $in_tree_ld = yes ; then
+  if test "$gcc_cv_gld_major_version" -eq 2 -a "$gcc_cv_gld_minor_version" -ge 14 -o "$gcc_cv_gld_major_version" -gt 2; then
+    ld_now_support=yes
+  fi
+elif test x$gcc_cv_ld != x; then
+  # Check if linker supports -z now
+  if $gcc_cv_ld --help 2>&1 | grep -- '-z now' > /dev/null; then
+    ld_now_support=yes
+  fi
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LD_NOW_SUPPORT `if test x"$ld_now_support" = xyes; then echo 1; else echo 0; fi`
+_ACEOF
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_now_support" >&5
+$as_echo "$ld_now_support" >&6; }
+
+# Check if the linker supports '-z relro'
+ld_relro_support=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking linker -z relro option" >&5
+$as_echo_n "checking linker -z relro option... " >&6; }
+if test x"$ld_is_gold" = xyes; then
+  ld_relro_support=yes
+elif test $in_tree_ld = yes ; then
+  if test "$gcc_cv_gld_major_version" -eq 2 -a "$gcc_cv_gld_minor_version" -ge 15 -o "$gcc_cv_gld_major_version" -gt 2; then
+    ld_relro_support=yes
+  fi
+elif test x$gcc_cv_ld != x; then
+  # Check if linker supports -z relro
+  if $gcc_cv_ld --help 2>&1 | grep -- '-z relro' > /dev/null; then
+    ld_relro_support=yes
+  fi
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LD_RELRO_SUPPORT `if test x"$ld_relro_support" = xyes; then echo 1; else echo 0; fi`
+_ACEOF
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_relro_support" >&5
+$as_echo "$ld_relro_support" >&6; }
+
 # Configure the subdirectories
 # AC_CONFIG_SUBDIRS($subdirs)
 
diff --git a/gcc/configure.ac b/gcc/configure.ac
index cb4be11facd..950a3f51370 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -7623,7 +7623,7 @@  if test x"$ld_is_gold" = xno; then
       ld_bndplt_support=yes
     fi
   elif test x$gcc_cv_ld != x; then
-    # Check if linker supports -a bndplt option
+    # Check if linker supports -z bndplt option
     if $gcc_cv_ld --help 2>&1 | grep -- '-z bndplt' > /dev/null; then
       ld_bndplt_support=yes
     fi
@@ -7724,6 +7724,46 @@  standards-compatible mode on s390 targets.])
   ;;
 esac
 
+# Check if the linker supports '-z now'
+ld_now_support=no
+AC_MSG_CHECKING(linker -z now option)
+if test x"$ld_is_gold" = xyes; then
+  ld_now_support=yes
+elif test $in_tree_ld = yes ; then
+  if test "$gcc_cv_gld_major_version" -eq 2 -a "$gcc_cv_gld_minor_version" -ge 14 -o "$gcc_cv_gld_major_version" -gt 2; then
+    ld_now_support=yes
+  fi
+elif test x$gcc_cv_ld != x; then
+  # Check if linker supports -z now
+  if $gcc_cv_ld --help 2>&1 | grep -- '-z now' > /dev/null; then
+    ld_now_support=yes
+  fi
+fi
+AC_DEFINE_UNQUOTED(HAVE_LD_NOW_SUPPORT,
+  [`if test x"$ld_now_support" = xyes; then echo 1; else echo 0; fi`],
+  [Define 0/1 if your linker supports -z now])
+AC_MSG_RESULT($ld_now_support)
+
+# Check if the linker supports '-z relro'
+ld_relro_support=no
+AC_MSG_CHECKING(linker -z relro option)
+if test x"$ld_is_gold" = xyes; then
+  ld_relro_support=yes
+elif test $in_tree_ld = yes ; then
+  if test "$gcc_cv_gld_major_version" -eq 2 -a "$gcc_cv_gld_minor_version" -ge 15 -o "$gcc_cv_gld_major_version" -gt 2; then
+    ld_relro_support=yes
+  fi
+elif test x$gcc_cv_ld != x; then
+  # Check if linker supports -z relro
+  if $gcc_cv_ld --help 2>&1 | grep -- '-z relro' > /dev/null; then
+    ld_relro_support=yes
+  fi
+fi
+AC_DEFINE_UNQUOTED(HAVE_LD_RELRO_SUPPORT,
+  [`if test x"$ld_relro_support" = xyes; then echo 1; else echo 0; fi`],
+  [Define 0/1 if your linker supports -z relro])
+AC_MSG_RESULT($ld_relro_support)
+
 # Configure the subdirectories
 # AC_CONFIG_SUBDIRS($subdirs)
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 03d93e6b185..ecd81c113ef 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -640,7 +640,7 @@  Objective-C and Objective-C++ Dialects}.
 -fasan-shadow-offset=@var{number}  -fsanitize-sections=@var{s1},@var{s2},...
 -fsanitize-undefined-trap-on-error  -fbounds-check
 -fcf-protection=@r{[}full@r{|}branch@r{|}return@r{|}none@r{|}check@r{]}
--fharden-compares -fharden-conditional-branches
+-fharden-compares -fharden-conditional-branches -fhardened
 -fstack-protector  -fstack-protector-all  -fstack-protector-strong
 -fstack-protector-explicit  -fstack-check
 -fstack-limit-register=@var{reg}  -fstack-limit-symbol=@var{sym}
@@ -17369,6 +17369,33 @@  condition, and to call @code{__builtin_trap} if the result is
 unexpected.  Use with @samp{-fharden-compares} to cover all
 conditionals.
 
+@opindex fhardened
+@item -fhardened
+Enable a set of flags for C and C++ that improve the security of the
+generated code without affecting its ABI.  The precise flags enabled
+may change between major releases of GCC, but are currently:
+
+@gccoptlist{
+-D_FORTIFY_SOURCE=3
+-D_GLIBCXX_ASSERTIONS
+-ftrivial-auto-var-init=pattern
+-fPIE  -pie  -Wl,-z,relro,-z,now
+-fstack-protector-strong
+-fstack-clash-protection
+-fcf-protection=full @r{(x86 GNU/Linux only)}
+}
+
+The list of options enabled by @option{-fhardened} can be generated using
+the @option{--help=hardened} option.
+
+When the system glibc is older than 2.35, @option{-D_FORTIFY_SOURCE=2}
+is used instead.
+
+@option{-fhardened} only enables a particular option if it wasn't
+already specified anywhere on the command line.  For instance,
+@option{-fhardened} @option{-fstack-protector} will only enable
+@option{-fstack-protector}, but not @option{-fstack-protector-strong}.
+
 @opindex fstack-protector
 @item -fstack-protector
 Emit extra code to check for buffer overflows, such as stack smashing
diff --git a/gcc/gcc.cc b/gcc/gcc.cc
index a9dd0eb655c..db87c400c02 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.  */
@@ -4597,10 +4604,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:
@@ -4976,6 +4993,26 @@  process_command (unsigned int decoded_options_count,
 #endif
     }
 
+  /* TODO: check if -static -pie works and maybe use it.  */
+  if (flag_hardened && !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", "*");
+	}
+    }
+
   /* 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)
diff --git a/gcc/opts.cc b/gcc/opts.cc
index ac81d4e4294..03e1fdaf8d3 100644
--- a/gcc/opts.cc
+++ b/gcc/opts.cc
@@ -43,6 +43,10 @@  along with GCC; see the file COPYING3.  If not see
 /* Set by -fcanon-prefix-map.  */
 bool flag_canon_prefix_map;
 
+/* Set by finish_options when flag_stack_protector was set only because of
+   -fhardened.  Yuck.  */
+bool flag_stack_protector_set_by_fhardened_p;
+
 static void set_Wstrict_aliasing (struct gcc_options *opts, int onoff);
 
 /* Names of fundamental debug info formats indexed by enum
@@ -1093,6 +1097,9 @@  finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
       opts->x_flag_section_anchors = 0;
     }
 
+  if (!opts_set->x_flag_auto_var_init && opts->x_flag_hardened)
+    opts->x_flag_auto_var_init = AUTO_INIT_PATTERN;
+
   if (!opts->x_flag_opts_finished)
     {
       /* We initialize opts->x_flag_pie to -1 so that targets can set a
@@ -1102,7 +1109,8 @@  finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
 	  /* We initialize opts->x_flag_pic to -1 so that we can tell if
 	     -fpic, -fPIC, -fno-pic or -fno-PIC is used.  */
 	  if (opts->x_flag_pic == -1)
-	    opts->x_flag_pie = DEFAULT_FLAG_PIE;
+	    opts->x_flag_pie = (opts->x_flag_hardened
+				? /*-fPIE*/ 2 : DEFAULT_FLAG_PIE);
 	  else
 	    opts->x_flag_pie = 0;
 	}
@@ -1117,9 +1125,23 @@  finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
     }
 
   /* We initialize opts->x_flag_stack_protect to -1 so that targets
-     can set a default value.  */
+     can set a default value.  With --enable-default-ssp or -fhardened
+     the default is -fstack-protector-strong.  */
   if (opts->x_flag_stack_protect == -1)
-    opts->x_flag_stack_protect = DEFAULT_FLAG_SSP;
+    {
+      /* This should check FRAME_GROWS_DOWNWARD, but on some targets it's
+	 defined in such a way that it uses flag_stack_protect which can't
+	 be used here.  Moreover, some targets like BPF don't support
+	 -fstack-protector at all but we don't know that here.  So remember
+	 that flag_stack_protect was set at the behest of -fhardened.  */
+      if (opts->x_flag_hardened)
+	{
+	  opts->x_flag_stack_protect = SPCT_FLAG_STRONG;
+	  flag_stack_protector_set_by_fhardened_p = true;
+	}
+      else
+	opts->x_flag_stack_protect = DEFAULT_FLAG_SSP;
+    }
 
   if (opts->x_optimize == 0)
     {
@@ -2461,6 +2483,29 @@  parse_and_check_patch_area (const char *arg, bool report_error,
   free (patch_area_arg);
 }
 
+/* Print options enabled by -fhardened.  */
+
+static void
+print_help_hardened ()
+{
+  printf ("%s\n", "The following options are enabled by -fhardened:");
+  printf ("  %s=%d\n", "-D_FORTIFY_SOURCE",
+	  (TARGET_GLIBC_MAJOR == 2 && TARGET_GLIBC_MINOR >= 35) ? 3 : 2);
+  printf ("  %s\n", "-D_GLIBCXX_ASSERTIONS");
+  printf ("  %s\n", "-ftrivial-auto-var-init=pattern");
+#ifdef HAVE_LD_PIE
+  printf ("  %s  %s\n", "-fPIE", "-pie");
+#endif
+  if (HAVE_LD_NOW_SUPPORT)
+    printf ("  %s\n", "-Wl,-z,now");
+  if (HAVE_LD_RELRO_SUPPORT)
+    printf ("  %s\n", "-Wl,-z,relro");
+  printf ("  %s\n", "-fstack-protector-strong");
+  printf ("  %s\n", "-fstack-clash-protection");
+  printf ("  %s\n", "-fcf-protection=full");
+  putchar ('\n');
+}
+
 /* Print help when OPT__help_ is set.  */
 
 void
@@ -2576,6 +2621,8 @@  print_help (struct gcc_options *opts, unsigned int lang_mask,
 	}
       else if (lang_flag != 0)
 	*pflags |= lang_flag;
+      else if (strncasecmp (a, "hardened", len) == 0)
+	print_help_hardened ();
       else
 	warning (0,
 		 "unrecognized argument to %<--help=%> option: %q.*s",
diff --git a/gcc/opts.h b/gcc/opts.h
index 00f377f9ca7..d89c5de8114 100644
--- a/gcc/opts.h
+++ b/gcc/opts.h
@@ -344,6 +344,7 @@  struct cl_option_handlers
 /* Hold command-line options associated with stack limitation.  */
 extern const char *opt_fstack_limit_symbol_arg;
 extern int opt_fstack_limit_register_no;
+extern bool flag_stack_protector_set_by_fhardened_p;
 
 /* Input file names.  */
 
diff --git a/gcc/testsuite/c-c++-common/fhardened-1.S b/gcc/testsuite/c-c++-common/fhardened-1.S
new file mode 100644
index 00000000000..026c7afbaad
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-1.S
@@ -0,0 +1,6 @@ 
+/* { dg-do preprocess { target pie } } */
+/* { dg-options "-fhardened" } */
+
+#if __PIE__ != 2
+# error "-fPIE not enabled"
+#endif
diff --git a/gcc/testsuite/c-c++-common/fhardened-1.c b/gcc/testsuite/c-c++-common/fhardened-1.c
new file mode 100644
index 00000000000..dd3cde93805
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-1.c
@@ -0,0 +1,14 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fhardened -O" } */
+
+#ifndef __SSP_STRONG__
+# error "-fstack-protector-strong not enabled"
+#endif
+
+#if _FORTIFY_SOURCE < 2
+# error "_FORTIFY_SOURCE not enabled"
+#endif
+
+#ifndef _GLIBCXX_ASSERTIONS
+# error "_GLIBCXX_ASSERTIONS not enabled"
+#endif
diff --git a/gcc/testsuite/c-c++-common/fhardened-10.c b/gcc/testsuite/c-c++-common/fhardened-10.c
new file mode 100644
index 00000000000..431bd5d3a80
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-10.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fhardened -D_FORTIFY_SOURCE=1" } */
+
+#if _FORTIFY_SOURCE != 1
+# error "_FORTIFY_SOURCE != 1"
+#endif
+
+#ifndef _GLIBCXX_ASSERTIONS
+# error "_GLIBCXX_ASSERTIONS disabled when it should not be"
+#endif
diff --git a/gcc/testsuite/c-c++-common/fhardened-11.c b/gcc/testsuite/c-c++-common/fhardened-11.c
new file mode 100644
index 00000000000..b8fd65a960c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-11.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fhardened -O -D_FORTIFY_SOURCE_ -D_GLIBCXX_ASSERTIONS_" } */
+
+#ifndef _FORTIFY_SOURCE
+# error "_FORTIFY_SOURCE disabled when it should not be"
+#endif
+
+#ifndef _GLIBCXX_ASSERTIONS
+# error "_GLIBCXX_ASSERTIONS disabled when it should not be"
+#endif
diff --git a/gcc/testsuite/c-c++-common/fhardened-12.c b/gcc/testsuite/c-c++-common/fhardened-12.c
new file mode 100644
index 00000000000..340a7bdad6d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-12.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fhardened -fdump-tree-gimple" } */
+
+int
+foo ()
+{
+  int i;
+  return i;
+}
+
+/* { dg-final { scan-tree-dump ".DEFERRED_INIT" "gimple" } } */
diff --git a/gcc/testsuite/c-c++-common/fhardened-13.c b/gcc/testsuite/c-c++-common/fhardened-13.c
new file mode 100644
index 00000000000..629ff6ff94b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-13.c
@@ -0,0 +1,6 @@ 
+/* { dg-do compile { target pie } } */
+/* { dg-options "-fhardened" } */
+
+#if __PIE__ != 2
+# error "-fPIE not enabled"
+#endif
diff --git a/gcc/testsuite/c-c++-common/fhardened-14.c b/gcc/testsuite/c-c++-common/fhardened-14.c
new file mode 100644
index 00000000000..bfcd000fe6a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-14.c
@@ -0,0 +1,6 @@ 
+/* { dg-do compile { target pie } } */
+/* { dg-options "-fhardened -fno-PIE" } */
+
+#ifdef __PIE__
+# error "PIE enabled when it should not be"
+#endif
diff --git a/gcc/testsuite/c-c++-common/fhardened-2.c b/gcc/testsuite/c-c++-common/fhardened-2.c
new file mode 100644
index 00000000000..aeba4e40a42
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-2.c
@@ -0,0 +1,9 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fhardened -fstack-protector" } */
+
+#ifdef __SSP_STRONG__
+# error "-fstack-protector-strong enabled when it should not be"
+#endif
+#ifndef __SSP__
+# error "-fstack-protector not enabled"
+#endif
diff --git a/gcc/testsuite/c-c++-common/fhardened-3.c b/gcc/testsuite/c-c++-common/fhardened-3.c
new file mode 100644
index 00000000000..105e013d734
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-3.c
@@ -0,0 +1,12 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fhardened -O0" } */
+/* Test that we don't get any diagnostic coming from libc headers.  */
+
+#include <stdio.h>
+
+/* The most useful C program known to man.  */
+
+int
+main ()
+{
+}
diff --git a/gcc/testsuite/c-c++-common/fhardened-5.c b/gcc/testsuite/c-c++-common/fhardened-5.c
new file mode 100644
index 00000000000..340a7bdad6d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-5.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fhardened -fdump-tree-gimple" } */
+
+int
+foo ()
+{
+  int i;
+  return i;
+}
+
+/* { dg-final { scan-tree-dump ".DEFERRED_INIT" "gimple" } } */
diff --git a/gcc/testsuite/c-c++-common/fhardened-6.c b/gcc/testsuite/c-c++-common/fhardened-6.c
new file mode 100644
index 00000000000..478caf9895b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-6.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fhardened -ftrivial-auto-var-init=uninitialized -fdump-tree-gimple" } */
+
+int
+foo ()
+{
+  int i;
+  return i;
+}
+
+/* { dg-final { scan-tree-dump-not ".DEFERRED_INIT" "gimple" } } */
diff --git a/gcc/testsuite/c-c++-common/fhardened-7.c b/gcc/testsuite/c-c++-common/fhardened-7.c
new file mode 100644
index 00000000000..daf91f2ca25
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-7.c
@@ -0,0 +1,7 @@ 
+/* { dg-do compile { target pie } } */
+/* { dg-options "-fhardened -fpie" } */
+
+/* -fpie takes precedence over -fhardened */
+#if __PIE__ != 1
+# error "__PIE__ != 1"
+#endif
diff --git a/gcc/testsuite/c-c++-common/fhardened-8.c b/gcc/testsuite/c-c++-common/fhardened-8.c
new file mode 100644
index 00000000000..87e2750efe2
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-8.c
@@ -0,0 +1,7 @@ 
+/* { dg-do compile { target pie } } */
+/* { dg-options "-fhardened -fPIC" } */
+
+/* -fPIC takes precedence over -fhardened */
+#ifdef __PIE__
+# error "PIE enabled when it should not be"
+#endif
diff --git a/gcc/testsuite/c-c++-common/fhardened-9.c b/gcc/testsuite/c-c++-common/fhardened-9.c
new file mode 100644
index 00000000000..f64e7eba56c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-9.c
@@ -0,0 +1,6 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fhardened -U_FORTIFY_SOURCE -U_GLIBCXX_ASSERTIONS" } */
+
+#if defined(_FORTIFY_SOURCE) || defined(_GLIBCXX_ASSERTIONS)
+# error "hardening enabled when it should not be"
+#endif
diff --git a/gcc/testsuite/gcc.misc-tests/help.exp b/gcc/testsuite/gcc.misc-tests/help.exp
index 52b9cb0ab90..15d618a2528 100644
--- a/gcc/testsuite/gcc.misc-tests/help.exp
+++ b/gcc/testsuite/gcc.misc-tests/help.exp
@@ -151,6 +151,8 @@  foreach cls { "ada" "c" "c++" "d" "fortran" "go" \
 # Listing only excludes gives empty results.
 check_for_options c "--help=^joined,^separate" "" "" ""
 
+check_for_options c "--help=hardened" "The following options are enabled by -fhardened" "" ""
+
 if [ info exists prev_columns ] {
     # Reset the enviroment variable to its oriuginal value.
     set env(COLUMNS) $prev_columns
diff --git a/gcc/testsuite/gcc.target/i386/cf_check-6.c b/gcc/testsuite/gcc.target/i386/cf_check-6.c
new file mode 100644
index 00000000000..73b78dce889
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/cf_check-6.c
@@ -0,0 +1,12 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fhardened -mno-manual-endbr" } */
+/* { dg-final { scan-assembler-times {\mendbr} 1 } } */
+/* Test that -fhardened enables CET.  */
+
+extern void bar (void) __attribute__((__cf_check__));
+
+void
+foo (void)
+{
+  bar ();
+}
diff --git a/gcc/toplev.cc b/gcc/toplev.cc
index db62e3e995e..68d744630e1 100644
--- a/gcc/toplev.cc
+++ b/gcc/toplev.cc
@@ -1575,6 +1575,12 @@  process_options (bool no_backend)
 		  "where the stack grows from lower to higher addresses");
       flag_stack_clash_protection = 0;
     }
+  else if (flag_hardened
+	   && !flag_stack_clash_protection
+	   /* Don't enable -fstack-clash-protection when -fstack-check=
+	      is used: it would result in confusing errors.  */
+	   && flag_stack_check == NO_STACK_CHECK)
+    flag_stack_clash_protection = 1;
 
   /* We cannot support -fstack-check= and -fstack-clash-protection at
      the same time.  */
@@ -1590,8 +1596,9 @@  process_options (bool no_backend)
      target already uses a soft frame pointer, the transition is trivial.  */
   if (!FRAME_GROWS_DOWNWARD && flag_stack_protect)
     {
-      warning_at (UNKNOWN_LOCATION, 0,
-		  "%<-fstack-protector%> not supported for this target");
+      if (!flag_stack_protector_set_by_fhardened_p)
+	warning_at (UNKNOWN_LOCATION, 0,
+		    "%<-fstack-protector%> not supported for this target");
       flag_stack_protect = 0;
     }
   if (!flag_stack_protect)