diff mbox series

[v5] gcc: Introduce -fhardened

Message ID ZVaAylotDULKTs/N@redhat.com
State New
Headers show
Series [v5] gcc: Introduce -fhardened | expand

Commit Message

Marek Polacek Nov. 16, 2023, 8:51 p.m. UTC
On Wed, Nov 15, 2023 at 01:25:27PM +0100, Jakub Jelinek wrote:
> On Fri, Nov 03, 2023 at 06:51:16PM -0400, Marek Polacek wrote:
> > +      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");
> > +	    }
> 
> I don't like the above in generic code, the fact that gcc was configured
> against glibc target headers doesn't mean it is targetting glibc.
> E.g. for most *-linux* targets, config/linux.opt provides the
> -mbionic/-mglibc/-muclibc/-mmusl options.
> 
> One ugly way around would be to do
> #ifdef OPTION_GLIBC
>   if (OPTION_GLIBC && TARGET_GLIBC_MAJOR == 2 && TARGET_GLIBC_MINOR >= 35)
>     cpp_define (parse_in, "_FORTIFY_SOURCE=3");
>   else
> #endif
>     cpp_define (parse_in, "_FORTIFY_SOURCE=2");
> (assuming OPTION_GLIBC at that point is already computed); a cleaner way
> would be to introduce a target hook for that, say
> fortify_source_default_level or something similar, where the default hook
> would return 2 and next to linux_libc_has_function one would override it
> for OPTION_GLIBC && TARGET_GLIBC_MAJOR == 2 && TARGET_GLIBC_MINOR >= 35
> to 3.  That way, in the future other targets (say *BSD) can choose to do
> something similar more easily.

Thanks, that's a good point.  In this version I've added a target hook.

On my system, -D_FORTIFY_SOURCE=3 will be used, and if I remove
linux_fortify_source_default_level it's =2 as expected.

The only problem was that it doesn't seem to be possible to use
targetm. in opts.cc -- I get an undefined reference.  But since
the opts.cc use is for --help only, it's not a big deal either way.

Bootstrapped/regtested on x86_64-pc-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=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.
	* 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/c-family/c-opts.cc                     | 39 +++++++++++++
 gcc/common.opt                             |  8 +++
 gcc/config.in                              | 18 ++++++
 gcc/config/bpf/bpf.cc                      |  8 ++-
 gcc/config/i386/i386-options.cc            | 17 +++++-
 gcc/config/linux-protos.h                  |  1 +
 gcc/config/linux.cc                        |  9 +++
 gcc/config/linux.h                         |  3 +
 gcc/configure                              | 68 +++++++++++++++++++++-
 gcc/configure.ac                           | 57 +++++++++++++++++-
 gcc/doc/invoke.texi                        | 49 +++++++++++++++-
 gcc/doc/tm.texi                            |  5 ++
 gcc/doc/tm.texi.in                         |  2 +
 gcc/gcc.cc                                 | 48 ++++++++++++++-
 gcc/opts.cc                                | 68 +++++++++++++++++++++-
 gcc/opts.h                                 |  1 +
 gcc/target.def                             |  7 +++
 gcc/targhooks.cc                           |  8 +++
 gcc/targhooks.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  | 12 ++++
 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-15.c  |  5 ++
 gcc/testsuite/c-c++-common/fhardened-2.c   | 12 ++++
 gcc/testsuite/c-c++-common/fhardened-3.c   | 14 +++++
 gcc/testsuite/c-c++-common/fhardened-4.c   |  4 ++
 gcc/testsuite/c-c++-common/fhardened-5.c   | 11 ++++
 gcc/testsuite/c-c++-common/fhardened-6.c   | 12 ++++
 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   |  9 +++
 gcc/testsuite/gcc.misc-tests/help.exp      |  2 +
 gcc/testsuite/gcc.target/i386/cf_check-6.c | 12 ++++
 gcc/toplev.cc                              | 25 +++++++-
 38 files changed, 588 insertions(+), 14 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-15.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-4.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: a671095c208c7cf5eb934b6a31bd9fb6f6640a6b

Comments

Jakub Jelinek Nov. 20, 2023, 4:32 p.m. UTC | #1
On Thu, Nov 16, 2023 at 03:51:22PM -0500, Marek Polacek wrote:
> Thanks, that's a good point.  In this version I've added a target hook.
> 
> On my system, -D_FORTIFY_SOURCE=3 will be used, and if I remove
> linux_fortify_source_default_level it's =2 as expected.
> 
> The only problem was that it doesn't seem to be possible to use
> targetm. in opts.cc -- I get an undefined reference.  But since
> the opts.cc use is for --help only, it's not a big deal either way.
> 
> Bootstrapped/regtested on x86_64-pc-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=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.
> 	* 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.

LGTM.

	Jakub
Marek Polacek Nov. 21, 2023, 3:41 p.m. UTC | #2
On Mon, Nov 20, 2023 at 05:32:47PM +0100, Jakub Jelinek wrote:
> LGTM.

Thanks a lot.  Since Richi seems to be fine with the patch as well,
I'll push it tomorrow AM if no comments.

Marek
Marek Polacek Nov. 23, 2023, 4:59 p.m. UTC | #3
On Tue, Nov 21, 2023 at 10:41:10AM -0500, Marek Polacek wrote:
> On Mon, Nov 20, 2023 at 05:32:47PM +0100, Jakub Jelinek wrote:
> > LGTM.
> 
> Thanks a lot.  Since Richi seems to be fine with the patch as well,
> I'll push it tomorrow AM if no comments.

Pushed.  I'll update changes.html now.

Thanks to everybody who contributed to this effort!

Marek
diff mbox series

Patch

diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc
index 10403c03bd6..d7faff10d66 100644
--- a/gcc/c-family/c-opts.cc
+++ b/gcc/c-family/c-opts.cc
@@ -22,6 +22,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "tm.h"
+#include "target.h"
 #include "c-target.h"
 #include "c-common.h"
 #include "memmodel.h"
@@ -1573,6 +1574,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++)
@@ -1590,6 +1594,41 @@  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)
+	    cpp_define_formatted (parse_in, "_FORTIFY_SOURCE=%u",
+				  targetm.fortify_source_default_level ());
+	  else if (optimize == 0)
+	    warning_at (UNKNOWN_LOCATION, OPT_Whardened,
+			"%<_FORTIFY_SOURCE%> is not enabled by %<-fhardened%> "
+			"because optimizations are turned off");
+	  else
+	    warning_at (UNKNOWN_LOCATION, OPT_Whardened,
+			"%<_FORTIFY_SOURCE%> is not enabled by %<-fhardened%> "
+			"because it was specified in %<-D%> or %<-U%>");
+	  if (!cxx_assert_seen_p)
+	    cpp_define (parse_in, "_GLIBCXX_ASSERTIONS");
+	  else
+	    warning_at (UNKNOWN_LOCATION, OPT_Whardened,
+			"%<_GLIBCXX_ASSERTIONS%> is not enabled by "
+			"%<-fhardened%> because it was specified in %<-D%> "
+			"or %<-U%>");
 	}
 
       cpp_stop_forcing_token_locations (parse_in);
diff --git a/gcc/common.opt b/gcc/common.opt
index d21db5d4a20..6dafe51e9f3 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -634,6 +634,10 @@  Wfree-nonheap-object
 Common Var(warn_free_nonheap_object) Init(1) Warning
 Warn when attempting to free a non-heap object.
 
+Whardened
+Common Var(warn_hardened) Init(1) Warning
+Warn when -fhardened did not enable an option from its set.
+
 Whsa
 Common Ignore Warning
 Does nothing.  Preserved for backward compatibility.
@@ -1819,6 +1823,10 @@  fguess-branch-probability
 Common Var(flag_guess_branch_prob) Optimization
 Enable guessing of branch probabilities.
 
+fhardened
+Common Driver Var(flag_hardened)
+Enable various security-relevant flags.
+
 fharden-compares
 Common Var(flag_harden_compares) Optimization
 Harden conditionals not used in branches, checking reversed conditions.
diff --git a/gcc/config.in b/gcc/config.in
index 866f9fff101..fb45b556fb0 100644
--- a/gcc/config.in
+++ b/gcc/config.in
@@ -1326,6 +1326,12 @@ 
 #endif
 
 
+/* Define 0/1 if -fhardened is supported */
+#ifndef USED_FOR_TARGET
+#undef HAVE_FHARDENED_SUPPORT
+#endif
+
+
 /* Define to 1 if you have the `fileno_unlocked' function. */
 #ifndef USED_FOR_TARGET
 #undef HAVE_FILENO_UNLOCKED
@@ -1734,6 +1740,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
@@ -1777,6 +1789,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 a0956a06972..223a43cbbb3 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 df7d24352d1..76adba672b9 100644
--- a/gcc/config/i386/i386-options.cc
+++ b/gcc/config/i386/i386-options.cc
@@ -3072,10 +3072,25 @@  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)
+    {
+      if (opts->x_flag_cf_protection == CF_NONE)
+	opts->x_flag_cf_protection = CF_FULL;
+      else if (opts->x_flag_cf_protection != CF_FULL)
+	warning_at (UNKNOWN_LOCATION, OPT_Whardened,
+		    "%<-fcf-protection=full%> is not enabled by "
+		    "%<-fhardened%> because it was specified on the command "
+		    "line");
+    }
+
   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/config/linux-protos.h b/gcc/config/linux-protos.h
index f2ea930ace7..834249867f7 100644
--- a/gcc/config/linux-protos.h
+++ b/gcc/config/linux-protos.h
@@ -22,3 +22,4 @@  extern bool linux_has_ifunc_p (void);
 extern bool linux_libc_has_function (enum function_class fn_class, tree);
 
 extern unsigned linux_libm_function_max_error (unsigned, machine_mode, bool);
+extern unsigned linux_fortify_source_default_level ();
diff --git a/gcc/config/linux.cc b/gcc/config/linux.cc
index 9114e55d44e..c8df6c7d840 100644
--- a/gcc/config/linux.cc
+++ b/gcc/config/linux.cc
@@ -49,3 +49,12 @@  linux_libm_function_max_error (unsigned cfn, machine_mode mode,
     return glibc_linux_libm_function_max_error (cfn, mode, boundary_p);
   return default_libm_function_max_error (cfn, mode, boundary_p);
 }
+
+unsigned
+linux_fortify_source_default_level ()
+{
+  if (OPTION_GLIBC && TARGET_GLIBC_MAJOR == 2 && TARGET_GLIBC_MINOR >= 35)
+    return 3;
+
+  return 2;
+}
diff --git a/gcc/config/linux.h b/gcc/config/linux.h
index ac56816bfca..79b6537dcf1 100644
--- a/gcc/config/linux.h
+++ b/gcc/config/linux.h
@@ -216,3 +216,6 @@  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 # define TARGET_LIBM_FUNCTION_MAX_ERROR linux_libm_function_max_error
 
 #endif
+
+#undef TARGET_FORTIFY_SOURCE_DEFAULT_LEVEL
+#define TARGET_FORTIFY_SOURCE_DEFAULT_LEVEL linux_fortify_source_default_level
diff --git a/gcc/configure b/gcc/configure
index ee97934ac4f..a02c23ae917 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -34583,7 +34583,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
@@ -34712,6 +34712,72 @@  $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; }
+
+case $target_os in
+linux* | gnu*)
+  # -fhardened is only supported on GNU/Linux.
+  fhardened_support=yes
+  ;;
+*)
+  fhardened_support=no
+  ;;
+esac
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_FHARDENED_SUPPORT `if test x"$fhardened_support" = xyes; then echo 1; else echo 0; fi`
+_ACEOF
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $fhardened_support" >&5
+$as_echo "$fhardened_support" >&6; }
+
 # Configure the subdirectories
 # AC_CONFIG_SUBDIRS($subdirs)
 
diff --git a/gcc/configure.ac b/gcc/configure.ac
index d0caf820648..86f1a4b9e4c 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -7741,7 +7741,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
@@ -7842,6 +7842,61 @@  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)
+
+case $target_os in
+linux* | gnu*)
+  # -fhardened is only supported on GNU/Linux.
+  fhardened_support=yes
+  ;;
+*)
+  fhardened_support=no
+  ;;
+esac
+
+AC_DEFINE_UNQUOTED(HAVE_FHARDENED_SUPPORT,
+  [`if test x"$fhardened_support" = xyes; then echo 1; else echo 0; fi`],
+  [Define 0/1 if -fhardened is supported])
+AC_MSG_RESULT($fhardened_support)
+
 # Configure the subdirectories
 # AC_CONFIG_SUBDIRS($subdirs)
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 1748afdbfe0..035adaa9dbf 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -366,7 +366,7 @@  Objective-C and Objective-C++ Dialects}.
 -Wformat-y2k  -Wframe-address
 -Wframe-larger-than=@var{byte-size}  -Wno-free-nonheap-object
 -Wno-if-not-aligned  -Wno-ignored-attributes
--Wignored-qualifiers  -Wno-incompatible-pointer-types
+-Wignored-qualifiers  -Wno-incompatible-pointer-types  -Whardened
 -Wimplicit  -Wimplicit-fallthrough  -Wimplicit-fallthrough=@var{n}
 -Wno-implicit-function-declaration  -Wno-implicit-int
 -Winfinite-recursion
@@ -641,7 +641,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
 -fharden-control-flow-redundancy  -fhardcfr-skip-leaf
 -fhardcfr-check-exceptions  -fhardcfr-check-returning-calls
 -fhardcfr-check-noreturn-calls=@r{[}always@r{|}no-xthrow@r{|}nothrow@r{|}never@r{]}
@@ -6869,6 +6869,18 @@  This warning is upgraded to an error by @option{-pedantic-errors}.
 Same as @option{-Wimplicit-int} and @option{-Wimplicit-function-declaration}.
 This warning is enabled by @option{-Wall}.
 
+@opindex Whardened
+@opindex Wno-hardened
+@item -Whardened
+Warn when @option{-fhardened} did not enable an option from its set (for
+which see @option{-fhardened}).  For instance, using @option{-fhardened}
+and @option{-fstack-protector} at the same time on the command line causes
+@option{-Whardened} to warn because @option{-fstack-protector-strong} is
+not enabled by @option{-fhardened}.
+
+This warning is enabled by default and has effect only when @option{-fhardened}
+is enabled.
+
 @opindex Wimplicit-fallthrough
 @opindex Wno-implicit-fallthrough
 @item -Wimplicit-fallthrough
@@ -17535,6 +17547,39 @@  made @option{no-xthrow} the default setting for this option: it excludes
 from the @code{noreturn} treatment only internal functions used to
 (re)raise exceptions, that are not affected by these optimizations.
 
+@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:
+
+@c Keep this in sync with print_help_hardened!
+@gccoptlist{
+-D_FORTIFY_SOURCE=3
+-D_GLIBCXX_ASSERTIONS
+-ftrivial-auto-var-init=zero
+-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.
+
+This option is intended to be used in production builds, not merely
+in debug builds.
+
+Currently, @option{-fhardened} is only supported on GNU/Linux targets.
+
+@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/doc/tm.texi b/gcc/doc/tm.texi
index d83ca73b1af..864aa210db0 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -5822,6 +5822,11 @@  This hook determines whether a function from a class of functions
 @code{(enum function_class)}@var{fcode} has a fast implementation.
 @end deftypefn
 
+@deftypefn {Target Hook} unsigned TARGET_FORTIFY_SOURCE_DEFAULT_LEVEL (void)
+This hook determines what value _FORTIFY_SOURCE will be set to when using
+the command-line option -fhardened.
+@end deftypefn
+
 @deftypefn {Target Hook} unsigned TARGET_LIBM_FUNCTION_MAX_ERROR (unsigned @var{cfn}, machine_mode @var{mode}, bool @var{boundary_p})
 This hook determines expected maximum errors for math functions measured
 in ulps (units of the last place).  0 means 0.5ulps precision (correctly
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 3d3ae12cc2f..245058eb887 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -4043,6 +4043,8 @@  macro, a reasonable default is used.
 
 @hook TARGET_LIBC_HAS_FAST_FUNCTION
 
+@hook TARGET_FORTIFY_SOURCE_DEFAULT_LEVEL
+
 @hook TARGET_LIBM_FUNCTION_MAX_ERROR
 
 @defmac NEXT_OBJC_RUNTIME
diff --git a/gcc/gcc.cc b/gcc/gcc.cc
index 51120c1489e..9f21ad9453e 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)
diff --git a/gcc/opts.cc b/gcc/opts.cc
index 33165c9e74f..5d5efaf1b9e 100644
--- a/gcc/opts.cc
+++ b/gcc/opts.cc
@@ -42,6 +42,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
@@ -1092,6 +1096,17 @@  finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
       opts->x_flag_section_anchors = 0;
     }
 
+  if (opts->x_flag_hardened)
+    {
+      if (!opts_set->x_flag_auto_var_init)
+	opts->x_flag_auto_var_init = AUTO_INIT_ZERO;
+      else if (opts->x_flag_auto_var_init != AUTO_INIT_ZERO)
+	warning_at (loc, OPT_Whardened,
+		    "%<-ftrivial-auto-var-init=zero%> is not enabled by "
+		    "%<-fhardened%> because it was specified on the command "
+		    "line");
+    }
+
   if (!opts->x_flag_opts_finished)
     {
       /* We initialize opts->x_flag_pie to -1 so that targets can set a
@@ -1101,7 +1116,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;
 	}
@@ -1116,9 +1132,29 @@  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;
+    }
+  else if (opts->x_flag_hardened
+	   && opts->x_flag_stack_protect != SPCT_FLAG_STRONG)
+    warning_at (UNKNOWN_LOCATION, OPT_Whardened,
+		"%<-fstack-protector-strong%> is not enabled by "
+		"%<-fhardened%> because it was specified on the command "
+		"line");
 
   if (opts->x_optimize == 0)
     {
@@ -2460,6 +2496,30 @@  parse_and_check_patch_area (const char *arg, bool report_error,
   free (patch_area_arg);
 }
 
+/* Print options enabled by -fhardened.  Keep this in sync with the manual!  */
+
+static void
+print_help_hardened ()
+{
+  printf ("%s\n", "The following options are enabled by -fhardened:");
+  /* Unfortunately, I can't seem to use targetm.fortify_source_default_level
+     here.  */
+  printf ("  %s\n", "-D_FORTIFY_SOURCE=3 (or =2 for glibc < 2.35)");
+  printf ("  %s\n", "-D_GLIBCXX_ASSERTIONS");
+  printf ("  %s\n", "-ftrivial-auto-var-init=zero");
+#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
@@ -2575,6 +2635,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/target.def b/gcc/target.def
index 0996da0f71a..579b9325212 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -2670,6 +2670,13 @@  DEFHOOK
  bool, (int fcode),
  default_libc_has_fast_function)
 
+DEFHOOK
+(fortify_source_default_level,
+ "This hook determines what value _FORTIFY_SOURCE will be set to when using\n\
+the command-line option -fhardened.",
+ unsigned, (void),
+ default_fortify_source_default_level)
+
 DEFHOOK
 (libm_function_max_error,
  "This hook determines expected maximum errors for math functions measured\n\
diff --git a/gcc/targhooks.cc b/gcc/targhooks.cc
index 4f5b240f8d6..6680e8ee1b7 100644
--- a/gcc/targhooks.cc
+++ b/gcc/targhooks.cc
@@ -1906,6 +1906,14 @@  bsd_libc_has_function (enum function_class fn_class,
   return false;
 }
 
+/* By default, -fhardened will add -D_FORTIFY_SOURCE=2.  */
+
+unsigned
+default_fortify_source_default_level ()
+{
+  return 2;
+}
+
 unsigned
 default_libm_function_max_error (unsigned, machine_mode, bool)
 {
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index 189549cb1c7..26a8297c785 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -219,6 +219,7 @@  extern bool default_libc_has_fast_function (int fcode);
 extern bool no_c99_libc_has_function (enum function_class, tree);
 extern bool gnu_libc_has_function (enum function_class, tree);
 extern bool bsd_libc_has_function (enum function_class, tree);
+extern unsigned default_fortify_source_default_level (void);
 extern unsigned default_libm_function_max_error (unsigned, machine_mode, bool);
 extern unsigned glibc_linux_libm_function_max_error (unsigned, machine_mode,
 						     bool);
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..9d0a5772d9e
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-1.S
@@ -0,0 +1,6 @@ 
+/* { dg-do preprocess { target { { *-*-linux* *-*-gnu* } && pie } } } */
+/* { dg-options "-fhardened -O" } */
+
+#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..7e6740655fe
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-1.c
@@ -0,0 +1,14 @@ 
+/* { dg-do compile { target *-*-linux* *-*-gnu* } } */
+/* { 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..badebc56440
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-10.c
@@ -0,0 +1,12 @@ 
+/* { dg-do compile { target *-*-linux* *-*-gnu* } } */
+/* { 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
+
+/* { dg-warning "._FORTIFY_SOURCE. is not enabled" "" { target *-*-* } 0 } */
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..d1a973d177a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-11.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile { target *-*-linux* *-*-gnu* } } */
+/* { 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..eb128f61ba3
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-12.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile { target *-*-linux* *-*-gnu* } } */
+/* { dg-options "-fhardened -O -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..8722e6d4b1a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-13.c
@@ -0,0 +1,6 @@ 
+/* { dg-do compile { target { { *-*-linux* *-*-gnu* } && pie } } } */
+/* { dg-options "-fhardened -O" } */
+
+#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..04d6c8ff954
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-14.c
@@ -0,0 +1,6 @@ 
+/* { dg-do compile { target { { *-*-linux* *-*-gnu* } && pie } } } */
+/* { dg-options "-fhardened -O -fno-PIE" } */
+
+#ifdef __PIE__
+# error "PIE enabled when it should not be"
+#endif
diff --git a/gcc/testsuite/c-c++-common/fhardened-15.c b/gcc/testsuite/c-c++-common/fhardened-15.c
new file mode 100644
index 00000000000..86dc5220159
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-15.c
@@ -0,0 +1,5 @@ 
+/* { dg-do compile { target *-*-linux* *-*-gnu* } } */
+/* { dg-require-stack-check "specific" } */
+/* { dg-options "-fhardened -O -fstack-check" } */
+
+/* { dg-warning ".-fstack-clash-protection. is not enabled by .-fhardened. because .-fstack-check. was specified" "" { target *-*-* } 0 } */
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..280ff96eb15
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-2.c
@@ -0,0 +1,12 @@ 
+/* { dg-do compile { target *-*-linux* *-*-gnu* } } */
+/* { 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
+
+/* { dg-warning ".-fstack-protector-strong. is not enabled" "" { target *-*-* } 0 } */
+/* { dg-warning "._FORTIFY_SOURCE. is not enabled" "" { target *-*-* } 0 } */
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..f2306ca5d33
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-3.c
@@ -0,0 +1,14 @@ 
+/* { dg-do compile { target *-*-linux* *-*-gnu* } } */
+/* { 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 ()
+{
+}
+
+/* { dg-warning "._FORTIFY_SOURCE. is not enabled" "" { target *-*-* } 0 } */
diff --git a/gcc/testsuite/c-c++-common/fhardened-4.c b/gcc/testsuite/c-c++-common/fhardened-4.c
new file mode 100644
index 00000000000..312fabb95a5
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-4.c
@@ -0,0 +1,4 @@ 
+/* { dg-do compile { target *-*-linux* *-*-gnu* } } */
+/* { dg-options "-fhardened -O0 -Wno-hardened" } */
+
+/* { dg-bogus "._FORTIFY_SOURCE. is not enabled" "" { target *-*-* } 0 } */
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..eb128f61ba3
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-5.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile { target *-*-linux* *-*-gnu* } } */
+/* { dg-options "-fhardened -O -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..d3cb7c8b353
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-6.c
@@ -0,0 +1,12 @@ 
+/* { dg-do compile { target *-*-linux* *-*-gnu* } } */
+/* { dg-options "-fhardened -O -ftrivial-auto-var-init=uninitialized -fdump-tree-gimple" } */
+
+int
+foo ()
+{
+  int i;
+  return i;
+}
+
+/* { dg-final { scan-tree-dump-not ".DEFERRED_INIT" "gimple" } } */
+/* { dg-warning ".-ftrivial-auto-var-init=zero. is not enabled" "" { target *-*-* } 0 } */
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..b47bf43f360
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-7.c
@@ -0,0 +1,7 @@ 
+/* { dg-do compile { target { { *-*-linux* *-*-gnu* } && pie } } } */
+/* { dg-options "-fhardened -O -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..85c9ad9103f
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-8.c
@@ -0,0 +1,7 @@ 
+/* { dg-do compile { target { { *-*-linux* *-*-gnu* } && pie } } } */
+/* { dg-options "-fhardened -O -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..4e4131f0bdd
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fhardened-9.c
@@ -0,0 +1,9 @@ 
+/* { dg-do compile { target *-*-linux* *-*-gnu* } } */
+/* { 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
+
+/* { dg-warning "._FORTIFY_SOURCE. is not enabled" "" { target *-*-* } 0 } */
+/* { dg-warning "._GLIBCXX_ASSERTIONS. is not enabled" "" { target *-*-* } 0 } */
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 8c3fcd337be..85450d97a1a 100644
--- a/gcc/toplev.cc
+++ b/gcc/toplev.cc
@@ -1568,6 +1568,13 @@  process_options ()
       flag_associative_math = 0;
     }
 
+  if (flag_hardened && !HAVE_FHARDENED_SUPPORT)
+    {
+      warning_at (UNKNOWN_LOCATION, 0,
+		  "%<-fhardened%> not supported for this target");
+      flag_hardened = 0;
+    }
+
   /* -fstack-clash-protection is not currently supported on targets
      where the stack grows up.  */
   if (flag_stack_clash_protection && !STACK_GROWS_DOWNWARD)
@@ -1577,6 +1584,19 @@  process_options ()
 		  "where the stack grows from lower to higher addresses");
       flag_stack_clash_protection = 0;
     }
+  else if (flag_hardened)
+    {
+      if (!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;
+      else if (flag_stack_check != NO_STACK_CHECK)
+	warning_at (UNKNOWN_LOCATION, OPT_Whardened,
+		    "%<-fstack-clash-protection%> is not enabled by "
+		    "%<-fhardened%> because %<-fstack-check%> was "
+		    "specified on the command line");
+    }
 
   /* We cannot support -fstack-check= and -fstack-clash-protection at
      the same time.  */
@@ -1592,8 +1612,9 @@  process_options ()
      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)