public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Richard Biener <richard.guenther@gmail.com>
To: Alexandre Oliva <oliva@adacore.com>
Cc: Alexandre Oliva via Gcc-patches <gcc-patches@gcc.gnu.org>
Subject: Re: [PATCH] hardened conditionals
Date: Tue, 26 Oct 2021 10:21:54 +0200	[thread overview]
Message-ID: <CAFiYyc1+=KN7Z6TUh7QAeObNZefNytXVWPjy84YuN-V2FXN64g@mail.gmail.com> (raw)
In-Reply-To: <orsfwtiyx2.fsf@lxoliva.fsfla.org>

On Fri, Oct 22, 2021 at 4:19 AM Alexandre Oliva <oliva@adacore.com> wrote:
>
> 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?

OK.

Thanks,
Richard.

>
> 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>

  reply	other threads:[~2021-10-26  8:22 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-10-09  3:30 Alexandre Oliva
2021-10-09  6:42 ` Richard Biener
2021-10-12  6:35   ` Alexandre Oliva
2021-10-12 12:23     ` Richard Biener
2021-10-13 18:54       ` Alexandre Oliva
2021-10-14  7:49         ` Richard Biener
2021-10-15 18:35           ` Alexandre Oliva
2021-10-18  9:17             ` Richard Biener
2021-10-20  8:50               ` Alexandre Oliva
2021-10-22  2:19                 ` Alexandre Oliva
2021-10-26  8:21                   ` Richard Biener [this message]
2021-10-28  4:04                     ` Alexandre Oliva
2021-10-30  9:44                       ` Alexandre Oliva

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAFiYyc1+=KN7Z6TUh7QAeObNZefNytXVWPjy84YuN-V2FXN64g@mail.gmail.com' \
    --to=richard.guenther@gmail.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=oliva@adacore.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).