[PATCH] hardened conditionals

Alexandre Oliva oliva@adacore.com
Fri Oct 22 02:19:37 GMT 2021


On Oct 20, 2021, Alexandre Oliva <oliva@adacore.com> wrote:

> I suppose it's a latent issue exposed by the patch,

I was mistaken.  Though I even had bisected the -fcompare-debug problem
back to a patch from back in May, that added a new sink_code pass before
store_merging, it was actually a bug in my patch, it was just a little
hard to hit with bootstrap-debug, but it came up with -fcompare-debug in
ways that misled me.

split_block remains slightly risky to use unless you know you have or
are going to insert nondebug stmts/insns in both blocks.  I've often
pondered warning in case split_block completes with only debug
stmts/insns in either block, but IIRC there are multiple passes that
split first and insert code afterwards, which have to be rearranged to
aovid the warning.

Anyway, here's the fixed patch.  Regstrapped on x86_64-linux-gnu, and
bootstrapped with an additional patch that enables both new passes.  Ok
to install?


hardened conditionals

From: Alexandre Oliva <oliva@adacore.com>

This patch introduces optional passes to harden conditionals used in
branches, and in computing boolean expressions, by adding redundant
tests of the reversed conditions, and trapping in case of unexpected
results.  Though in abstract machines the redundant tests should never
fail, CPUs may be led to misbehave under certain kinds of attacks,
such as of power deprivation, and these tests reduce the likelihood of
going too far down an unexpected execution path.


for  gcc/ChangeLog

	* common.opt (fharden-compares): New.
	(fharden-conditional-branches): New.
	* doc/invoke.texi: Document new options.
	* gimple-harden-conditionals.cc: New.
	* passes.def: Add new passes.
	* tree-pass.h (make_pass_harden_compares): Declare.
	(make_pass_harden_conditional_branches): Declare.

for  gcc/ada/ChangeLog

	* doc/gnat_rm/security_hardening_features.rst
	(Hardened Conditionals): New.

for  gcc/testsuite/ChangeLog

	* c-c++-common/torture/harden-comp.c: New.
	* c-c++-common/torture/harden-cond.c: New.
---
 gcc/Makefile.in                                    |    1 
 .../doc/gnat_rm/security_hardening_features.rst    |   40 ++
 gcc/common.opt                                     |    8 
 gcc/doc/invoke.texi                                |   19 +
 gcc/gimple-harden-conditionals.cc                  |  439 ++++++++++++++++++++
 gcc/passes.def                                     |    2 
 gcc/testsuite/c-c++-common/torture/harden-comp.c   |   14 +
 gcc/testsuite/c-c++-common/torture/harden-cond.c   |   18 +
 gcc/tree-pass.h                                    |    3 
 9 files changed, 544 insertions(+)
 create mode 100644 gcc/gimple-harden-conditionals.cc
 create mode 100644 gcc/testsuite/c-c++-common/torture/harden-comp.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/harden-cond.c

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index f36ffa4740b78..a79ff93dd5999 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1389,6 +1389,7 @@ OBJS = \
 	gimple-if-to-switch.o \
 	gimple-iterator.o \
 	gimple-fold.o \
+	gimple-harden-conditionals.o \
 	gimple-laddress.o \
 	gimple-loop-interchange.o \
 	gimple-loop-jam.o \
diff --git a/gcc/ada/doc/gnat_rm/security_hardening_features.rst b/gcc/ada/doc/gnat_rm/security_hardening_features.rst
index 1c46e3a4c7b88..52240d7e3dd54 100644
--- a/gcc/ada/doc/gnat_rm/security_hardening_features.rst
+++ b/gcc/ada/doc/gnat_rm/security_hardening_features.rst
@@ -87,3 +87,43 @@ types and subtypes, may be silently ignored.  Specifically, it is not
 currently recommended to rely on any effects this pragma might be
 expected to have when calling subprograms through access-to-subprogram
 variables.
+
+
+.. Hardened Conditionals:
+
+Hardened Conditionals
+=====================
+
+GNAT can harden conditionals to protect against control flow attacks.
+
+This is accomplished by two complementary transformations, each
+activated by a separate command-line option.
+
+The option *-fharden-compares* enables hardening of compares that
+compute results stored in variables, adding verification that the
+reversed compare yields the opposite result.
+
+The option *-fharden-conditional-branches* enables hardening of
+compares that guard conditional branches, adding verification of the
+reversed compare to both execution paths.
+
+These transformations are introduced late in the compilation pipeline,
+long after boolean expressions are decomposed into separate compares,
+each one turned into either a conditional branch or a compare whose
+result is stored in a boolean variable or temporary.  Compiler
+optimizations, if enabled, may also turn conditional branches into
+stored compares, and vice-versa.  Conditionals may also be optimized
+out entirely, if their value can be determined at compile time, and
+occasionally multiple compares can be combined into one.
+
+It is thus difficult to predict which of these two options will affect
+a specific compare operation expressed in source code.  Using both
+options ensures that every compare that is not optimized out will be
+hardened.
+
+The addition of reversed compares can be observed by enabling the dump
+files of the corresponding passes, through command line options
+*-fdump-tree-hardcmp* and *-fdump-tree-hardcbr*, respectively.
+
+They are separate options, however, because of the significantly
+different performance impact of the hardening transformations.
diff --git a/gcc/common.opt b/gcc/common.opt
index a2af7fb36e0dd..7144755f66d61 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1722,6 +1722,14 @@ fguess-branch-probability
 Common Var(flag_guess_branch_prob) Optimization
 Enable guessing of branch probabilities.
 
+fharden-compares
+Common Var(flag_harden_compares) Optimization
+Harden conditionals not used in branches, checking reversed conditions.
+
+fharden-conditional-branches
+Common Var(flag_harden_conditional_branches) Optimization
+Harden conditional branches by checking reversed conditions.
+
 ; 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/doc/invoke.texi b/gcc/doc/invoke.texi
index 0cc8a8edd058c..a9129a6f11c64 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -596,6 +596,7 @@ Objective-C and Objective-C++ Dialects}.
 -fasan-shadow-offset=@var{number}  -fsanitize-sections=@var{s1},@var{s2},... @gol
 -fsanitize-undefined-trap-on-error  -fbounds-check @gol
 -fcf-protection=@r{[}full@r{|}branch@r{|}return@r{|}none@r{|}check@r{]} @gol
+-fharden-compares -fharden-conditional-branches @gol
 -fstack-protector  -fstack-protector-all  -fstack-protector-strong @gol
 -fstack-protector-explicit  -fstack-check @gol
 -fstack-limit-register=@var{reg}  -fstack-limit-symbol=@var{sym} @gol
@@ -15532,6 +15533,24 @@ which functions and calls should be skipped from instrumentation
 Currently the x86 GNU/Linux target provides an implementation based
 on Intel Control-flow Enforcement Technology (CET).
 
+@item -fharden-compares
+@opindex fharden-compares
+For every logical test that survives gimple optimizations and is
+@emph{not} the condition in a conditional branch (for example,
+conditions tested for conditional moves, or to store in boolean
+variables), emit extra code to compute and verify the reversed
+condition, and to call @code{__builtin_trap} if the results do not
+match.  Use with @samp{-fharden-conditional-branches} to cover all
+conditionals.
+
+@item -fharden-conditional-branches
+@opindex fharden-conditional-branches
+For every non-vectorized conditional branch that survives gimple
+optimizations, emit extra code to compute and verify the reversed
+condition, and to call @code{__builtin_trap} if the result is
+unexpected.  Use with @samp{-fharden-compares} to cover all
+conditionals.
+
 @item -fstack-protector
 @opindex fstack-protector
 Emit extra code to check for buffer overflows, such as stack smashing
diff --git a/gcc/gimple-harden-conditionals.cc b/gcc/gimple-harden-conditionals.cc
new file mode 100644
index 0000000000000..8916420d7dfe9
--- /dev/null
+++ b/gcc/gimple-harden-conditionals.cc
@@ -0,0 +1,439 @@
+/* Harden conditionals.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Alexandre Oliva <oliva@adacore.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "tree.h"
+#include "fold-const.h"
+#include "gimple.h"
+#include "gimplify.h"
+#include "tree-pass.h"
+#include "ssa.h"
+#include "gimple-iterator.h"
+#include "tree-cfg.h"
+#include "basic-block.h"
+#include "cfghooks.h"
+#include "cfgloop.h"
+#include "diagnostic.h"
+#include "intl.h"
+
+namespace {
+
+/* These passes introduces redundant, but reversed conditionals at
+   compares, such as those used in conditional branches, and those
+   that compute boolean results.  This doesn't make much sense for
+   abstract CPUs, but this kind of hardening may avoid undesirable
+   execution paths on actual CPUs under such attacks as of power
+   deprivation.  */
+
+/* Define a pass to harden conditionals other than branches.  */
+
+const pass_data pass_data_harden_compares = {
+  GIMPLE_PASS,
+  "hardcmp",
+  OPTGROUP_NONE,
+  TV_NONE,
+  PROP_cfg | PROP_ssa, // properties_required
+  0,	    // properties_provided
+  0,	    // properties_destroyed
+  0,	    // properties_start
+  TODO_update_ssa
+  | TODO_cleanup_cfg
+  | TODO_verify_il, // properties_finish
+};
+
+class pass_harden_compares : public gimple_opt_pass
+{
+public:
+  pass_harden_compares (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_harden_compares, ctxt)
+  {}
+  opt_pass *clone () { return new pass_harden_compares (m_ctxt); }
+  virtual bool gate (function *) {
+    return flag_harden_compares;
+  }
+  virtual unsigned int execute (function *);
+};
+
+/* Define a pass to harden conditionals in branches.  This pass must
+   run after the above, otherwise it will re-harden the checks
+   introduced by the above.  */
+
+const pass_data pass_data_harden_conditional_branches = {
+  GIMPLE_PASS,
+  "hardcbr",
+  OPTGROUP_NONE,
+  TV_NONE,
+  PROP_cfg | PROP_ssa, // properties_required
+  0,	    // properties_provided
+  0,	    // properties_destroyed
+  0,	    // properties_start
+  TODO_update_ssa
+  | TODO_cleanup_cfg
+  | TODO_verify_il, // properties_finish
+};
+
+class pass_harden_conditional_branches : public gimple_opt_pass
+{
+public:
+  pass_harden_conditional_branches (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_harden_conditional_branches, ctxt)
+  {}
+  opt_pass *clone () { return new pass_harden_conditional_branches (m_ctxt); }
+  virtual bool gate (function *) {
+    return flag_harden_conditional_branches;
+  }
+  virtual unsigned int execute (function *);
+};
+
+}
+
+/* If VAL is an SSA name, return an SSA name holding the same value,
+   but without the compiler's knowing that it holds the same value, so
+   that uses thereof can't be optimized the way VAL might.  Insert
+   stmts that initialize it before *GSIP, with LOC.
+
+   Otherwise, VAL must be an invariant, returned unchanged.  */
+
+static inline tree
+detach_value (location_t loc, gimple_stmt_iterator *gsip, tree val)
+{
+  if (TREE_CONSTANT (val) || TREE_CODE (val) != SSA_NAME)
+    {
+      gcc_checking_assert (is_gimple_min_invariant (val));
+      return val;
+    }
+
+  tree ret = copy_ssa_name (val);
+
+  /* Output asm ("" : "=g" (ret) : "0" (val));  */
+  vec<tree, va_gc> *inputs = NULL;
+  vec<tree, va_gc> *outputs = NULL;
+  vec_safe_push (outputs,
+		 build_tree_list
+		 (build_tree_list
+		  (NULL_TREE, build_string (2, "=g")),
+		  ret));
+  vec_safe_push (inputs,
+		 build_tree_list
+		 (build_tree_list
+		  (NULL_TREE, build_string (1, "0")),
+		  val));
+  gasm *detach = gimple_build_asm_vec ("", inputs, outputs,
+				       NULL, NULL);
+  gimple_set_location (detach, loc);
+  gsi_insert_before (gsip, detach, GSI_SAME_STMT);
+
+  SSA_NAME_DEF_STMT (ret) = detach;
+
+  return ret;
+}
+
+/* Build a cond stmt out of COP, LHS, RHS, insert it before *GSIP with
+   location LOC.  *GSIP must be at the end of a basic block.  The succ
+   edge out of the block becomes the true or false edge opposite to
+   that in FLAGS.  Create a new block with a single trap stmt, in the
+   cold partition if the function is partitioned,, and a new edge to
+   it as the other edge for the cond.  */
+
+static inline void
+insert_check_and_trap (location_t loc, gimple_stmt_iterator *gsip,
+		       int flags, enum tree_code cop, tree lhs, tree rhs)
+{
+  basic_block chk = gsi_bb (*gsip);
+
+  gcond *cond = gimple_build_cond (cop, lhs, rhs, NULL, NULL);
+  gimple_set_location (cond, loc);
+  gsi_insert_before (gsip, cond, GSI_SAME_STMT);
+
+  basic_block trp = create_empty_bb (chk);
+
+  gimple_stmt_iterator gsit = gsi_after_labels (trp);
+  gcall *trap = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0);
+  gimple_set_location (trap, loc);
+  gsi_insert_before (&gsit, trap, GSI_SAME_STMT);
+
+  if (dump_file)
+    fprintf (dump_file,
+	     "Adding reversed compare to block %i, and trap to block %i\n",
+	     chk->index, trp->index);
+
+  if (BB_PARTITION (chk))
+    BB_SET_PARTITION (trp, BB_COLD_PARTITION);
+
+  int true_false_flag = flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE);
+  gcc_assert (true_false_flag);
+  int neg_true_false_flag = (~flags) & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE);
+
+  /* Remove the fallthru bit, and set the truth value for the
+     preexisting edge and for the newly-created one.  In hardcbr,
+     FLAGS is taken from the edge of the original cond expr that we're
+     dealing with, so the reversed compare is expected to yield the
+     negated result, and the same result calls for a trap.  In
+     hardcmp, we're comparing the boolean results of the original and
+     of the reversed compare, so we're passed FLAGS to trap on
+     equality.  */
+  single_succ_edge (chk)->flags &= ~EDGE_FALLTHRU;
+  single_succ_edge (chk)->flags |= neg_true_false_flag;
+  edge e = make_edge (chk, trp, true_false_flag);
+  e->goto_locus = loc;
+
+  if (dom_info_available_p (CDI_DOMINATORS))
+    set_immediate_dominator (CDI_DOMINATORS, trp, chk);
+  if (current_loops)
+    add_bb_to_loop (trp, current_loops->tree_root);
+}
+
+/* Split edge E, and insert_check_and_trap (see above) in the
+   newly-created block, using detached copies of LHS's and RHS's
+   values (see detach_value above) for the COP compare.  */
+
+static inline void
+insert_edge_check_and_trap (location_t loc, edge e,
+			    enum tree_code cop, tree lhs, tree rhs)
+{
+  int flags = e->flags;
+  basic_block src = e->src;
+  basic_block dest = e->dest;
+  location_t eloc = e->goto_locus;
+
+  basic_block chk = split_edge (e);
+  e = NULL;
+
+  single_pred_edge (chk)->goto_locus = loc;
+  single_succ_edge (chk)->goto_locus = eloc;
+
+  if (dump_file)
+    fprintf (dump_file,
+	     "Splitting edge %i->%i into block %i\n",
+	     src->index, dest->index, chk->index);
+
+  gimple_stmt_iterator gsik = gsi_after_labels (chk);
+
+  bool same_p = (lhs == rhs);
+  lhs = detach_value (loc, &gsik, lhs);
+  rhs = same_p ? lhs : detach_value (loc, &gsik, rhs);
+
+  insert_check_and_trap (loc, &gsik, flags, cop, lhs, rhs);
+}
+
+/* Harden cond stmts at the end of FUN's blocks.  */
+
+unsigned int
+pass_harden_conditional_branches::execute (function *fun)
+{
+  basic_block bb;
+  FOR_EACH_BB_REVERSE_FN (bb, fun)
+    {
+      gimple_stmt_iterator gsi = gsi_last_bb (bb);
+
+      if (gsi_end_p (gsi))
+	continue;
+
+      gcond *cond = dyn_cast <gcond *> (gsi_stmt (gsi));
+      if (!cond)
+	continue;
+
+      /* Turn:
+
+	 if (x op y) goto l1; else goto l2;
+
+	 into:
+
+	 if (x op y) goto l1'; else goto l2';
+	 l1': if (x' cop y') goto l1'trap; else goto l1;
+	 l1'trap: __builtin_trap ();
+	 l2': if (x' cop y') goto l2; else goto l2'trap;
+	 l2'trap: __builtin_trap ();
+
+	 where cop is a complementary boolean operation to op; l1', l1'trap,
+	 l2' and l2'trap are newly-created labels; and x' and y' hold the same
+	 value as x and y, but in a way that does not enable the compiler to
+	 optimize the redundant compare away.
+      */
+
+      enum tree_code op = gimple_cond_code (cond);
+      tree lhs = gimple_cond_lhs (cond);
+      tree rhs = gimple_cond_rhs (cond);
+      location_t loc = gimple_location (cond);
+
+      enum tree_code cop = invert_tree_comparison (op, HONOR_NANS (lhs));
+
+      if (cop == ERROR_MARK)
+	/* ??? Can we do better?  */
+	continue;
+
+      insert_edge_check_and_trap (loc, EDGE_SUCC (bb, 0), cop, lhs, rhs);
+      insert_edge_check_and_trap (loc, EDGE_SUCC (bb, 1), cop, lhs, rhs);
+    }
+
+  return 0;
+}
+
+/* Instantiate a hardcbr pass.  */
+
+gimple_opt_pass *
+make_pass_harden_conditional_branches (gcc::context *ctxt)
+{
+  return new pass_harden_conditional_branches (ctxt);
+}
+
+/* Harden boolean-yielding compares in FUN.  */
+
+unsigned int
+pass_harden_compares::execute (function *fun)
+{
+  basic_block bb;
+  /* Go backwards over BBs and stmts, so that, even if we split the
+     block multiple times to insert a cond_expr after each compare we
+     find, we remain in the same block, visiting every preexisting
+     stmt exactly once, and not visiting newly-added blocks or
+     stmts.  */
+  FOR_EACH_BB_REVERSE_FN (bb, fun)
+    for (gimple_stmt_iterator gsi = gsi_last_bb (bb);
+	 !gsi_end_p (gsi); gsi_prev (&gsi))
+      {
+	gassign *asgn = dyn_cast <gassign *> (gsi_stmt (gsi));
+	if (!asgn)
+	  continue;
+
+	/* Turn:
+
+	   z = x op y;
+
+	   into:
+
+	   z = x op y;
+	   z' = x' cop y';
+	   if (z == z') __builtin_trap ();
+
+	   where cop is a complementary boolean operation to op; and x'
+	   and y' hold the same value as x and y, but in a way that does
+	   not enable the compiler to optimize the redundant compare
+	   away.
+	*/
+
+	enum tree_code op = gimple_assign_rhs_code (asgn);
+
+	enum tree_code cop;
+
+	switch (op)
+	  {
+	  case EQ_EXPR:
+	  case NE_EXPR:
+	  case GT_EXPR:
+	  case GE_EXPR:
+	  case LT_EXPR:
+	  case LE_EXPR:
+	  case LTGT_EXPR:
+	  case UNEQ_EXPR:
+	  case UNGT_EXPR:
+	  case UNGE_EXPR:
+	  case UNLT_EXPR:
+	  case UNLE_EXPR:
+	  case ORDERED_EXPR:
+	  case UNORDERED_EXPR:
+	    cop = invert_tree_comparison (op,
+					  HONOR_NANS
+					  (gimple_assign_rhs1 (asgn)));
+
+	    if (cop == ERROR_MARK)
+	      /* ??? Can we do better?  */
+	      continue;
+
+	    break;
+
+	    /* ??? Maybe handle these too?  */
+	  case TRUTH_NOT_EXPR:
+	    /* ??? The code below assumes binary ops, it would have to
+	       be adjusted for TRUTH_NOT_EXPR, since it's unary.  */
+	  case TRUTH_ANDIF_EXPR:
+	  case TRUTH_ORIF_EXPR:
+	  case TRUTH_AND_EXPR:
+	  case TRUTH_OR_EXPR:
+	  case TRUTH_XOR_EXPR:
+	  default:
+	    continue;
+	  }
+
+	/* These are the operands for the verification.  */
+	tree lhs = gimple_assign_lhs (asgn);
+	tree op1 = gimple_assign_rhs1 (asgn);
+	tree op2 = gimple_assign_rhs2 (asgn);
+	location_t loc = gimple_location (asgn);
+
+	/* Vector booleans can't be used in conditional branches.  ???
+	   Can we do better?  How to reduce compare and
+	   reversed-compare result vectors to a single boolean?  */
+	if (VECTOR_TYPE_P (TREE_TYPE (op1)))
+	  continue;
+
+	gcc_checking_assert (TREE_CODE (TREE_TYPE (lhs)) == BOOLEAN_TYPE);
+
+	tree rhs = copy_ssa_name (lhs);
+
+	gimple_stmt_iterator gsi_split = gsi;
+	/* Don't separate the original assignment from debug stmts
+	   that might be associated with it, and arrange to split the
+	   block after debug stmts, so as to make sure the split block
+	   won't be debug stmts only.  */
+	gsi_next_nondebug (&gsi_split);
+
+	bool same_p = (op1 == op2);
+	op1 = detach_value (loc, &gsi_split, op1);
+	op2 = same_p ? op1 : detach_value (loc, &gsi_split, op2);
+
+	gassign *asgnck = gimple_build_assign (rhs, cop, op1, op2);
+	gimple_set_location (asgnck, loc);
+	gsi_insert_before (&gsi_split, asgnck, GSI_SAME_STMT);
+
+	/* We wish to insert a cond_expr after the compare, so arrange
+	   for it to be at the end of a block if it isn't.  */
+	if (!gsi_end_p (gsi_split))
+	  {
+	    gsi_prev (&gsi_split);
+	    split_block (bb, gsi_stmt (gsi_split));
+	    gsi_next (&gsi_split);
+	    gcc_checking_assert (gsi_end_p (gsi_split));
+
+	    single_succ_edge (bb)->goto_locus = loc;
+
+	    if (dump_file)
+	      fprintf (dump_file, "Splitting block %i\n", bb->index);
+	  }
+
+	gcc_checking_assert (single_succ_p (bb));
+
+	insert_check_and_trap (loc, &gsi_split, EDGE_TRUE_VALUE,
+			       EQ_EXPR, lhs, rhs);
+      }
+
+  return 0;
+}
+
+/* Instantiate a hardcmp pass.  */
+
+gimple_opt_pass *
+make_pass_harden_compares (gcc::context *ctxt)
+{
+  return new pass_harden_compares (ctxt);
+}
diff --git a/gcc/passes.def b/gcc/passes.def
index c11c237f6d204..268d8595a401e 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -421,6 +421,8 @@ along with GCC; see the file COPYING3.  If not see
   NEXT_PASS (pass_lower_resx);
   NEXT_PASS (pass_nrv);
   NEXT_PASS (pass_gimple_isel);
+  NEXT_PASS (pass_harden_conditional_branches);
+  NEXT_PASS (pass_harden_compares);
   NEXT_PASS (pass_cleanup_cfg_post_optimizing);
   NEXT_PASS (pass_warn_function_noreturn);
   NEXT_PASS (pass_warn_access);
diff --git a/gcc/testsuite/c-c++-common/torture/harden-comp.c b/gcc/testsuite/c-c++-common/torture/harden-comp.c
new file mode 100644
index 0000000000000..1ee0b3663443d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/harden-comp.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-fharden-compares -fdump-tree-hardcmp -ffat-lto-objects" } */
+
+int
+f (int i, int j)
+{
+  return i < j;
+}
+
+/* { dg-final { scan-tree-dump-times "Splitting block" 1 "hardcmp" } } */
+/* { dg-final { scan-tree-dump-times "Adding reversed compare" 1 "hardcmp" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_trap" 1 "hardcmp" } } */
+/* { dg-final { scan-tree-dump-times "_\[0-9\]* = i_\[0-9\]*\[(\]D\[)\] < j_\[0-9\]*\[(\]D\[)\];" 1 "hardcmp" } } */
+/* { dg-final { scan-tree-dump-times "_\[0-9\]* = i_\[0-9\]* >= j_\[0-9\]*;" 1 "hardcmp" } } */
diff --git a/gcc/testsuite/c-c++-common/torture/harden-cond.c b/gcc/testsuite/c-c++-common/torture/harden-cond.c
new file mode 100644
index 0000000000000..86de8e155ed38
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/harden-cond.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-fharden-conditional-branches -fdump-tree-hardcbr -ffat-lto-objects" } */
+
+extern int f1 (void);
+extern int f2 (void);
+
+
+int
+f (int i, int j)
+{
+  return (i < j) ? f1 () : f2 ();
+}
+
+/* { dg-final { scan-tree-dump-times "Splitting edge" 2 "hardcbr" } } */
+/* { dg-final { scan-tree-dump-times "Adding reversed compare" 2 "hardcbr" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_trap" 2 "hardcbr" } } */
+/* { dg-final { scan-tree-dump-times "if \[(\]i_\[0-9\]*\[(\]D\[)\] < j_\[0-9\]*\[(\]D\[)\]\[)\]" 1 "hardcbr" } } */
+/* { dg-final { scan-tree-dump-times "if \[(\]i_\[0-9\]* >= j_\[0-9\]*\[)\]" 2 "hardcbr" } } */
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index d379769a94364..e807ad855efd1 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -644,6 +644,9 @@ extern gimple_opt_pass *make_pass_update_address_taken (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_convert_switch (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_lower_vaarg (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_gimple_isel (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_harden_compares (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_harden_conditional_branches (gcc::context
+							       *ctxt);
 
 /* Current optimization pass.  */
 extern opt_pass *current_pass;


-- 
Alexandre Oliva, happy hacker                https://FSFLA.org/blogs/lxo/
   Free Software Activist                       GNU Toolchain Engineer
Disinformation flourishes because many people care deeply about injustice
but very few check the facts.  Ask me about <https://stallmansupport.org>


More information about the Gcc-patches mailing list