public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] Don't fold in save_expr (PR sanitizer/80536, PR sanitizer/80386)
@ 2017-05-11 13:54 Marek Polacek
  2017-05-12  7:39 ` Richard Biener
  0 siblings, 1 reply; 6+ messages in thread
From: Marek Polacek @ 2017-05-11 13:54 UTC (permalink / raw)
  To: GCC Patches, Jakub Jelinek, Jason Merrill, Joseph Myers

I had an interesting time coming to grips with these two PRs.  But it
essentially comes down to the fold call in save_expr.  With that, we can call
fold() on an expression whose operands weren't folded yet, but that is what
code in fold_binary_loc assumes.  Since fold() is not recursive, we could end
up there with expressions such as
i * ((unsigned long) -0 + 1) * 2
causing various sorts of problems: turning valid code into invalid, infinite
recursion, etc.

It's not clear why save_expr folds.  I did some archeology but all I could
find was r67189 (that's 2003) which had:

-  tree t = expr;
+  tree t = fold (expr);
   tree inner;
 
-  /* Don't fold a COMPONENT_EXPR: if the operand was a CONSTRUCTOR (the
-     only time it will fold), it can cause problems with PLACEHOLDER_EXPRs
-     in Ada.  Moreover, it isn't at all clear why we fold here at all.  */
-  if (TREE_CODE (t) != COMPONENT_REF)
-    t = fold (t);

so it wasn't clear even 14 years ago.  But now we know the call is harmful.

Now, fold() doesn't recurse, but can it happen that it folds something anyway?
And maybe turn the expression into an invariant so that we don't need to wrap
it in a SAVE_EXPR?  Turns out it can, but in the C FE, which either uses
c_save_expr that c_fully_folds, or calls save_expr when in_late_binary_op (so
the operands have already been folded), it's very rare, and can only be triggered
by code such as

void
f (int i)
{
  int (*a)[i];
  int x[sizeof (*a)];
}

so I'm not very worried about that.  For C++, Jakub suggested to add SAVE_EXPR
handling to cp_fold.

Future work: get rid of c_save_expr, only use save_expr, and teach c_fully_fold
how to fold SAVE_EXPR (once).  Although there's this c_wrap_maybe_const
business...

Bootstrapped/regtested on x86_64-linux, ok for trunk?

2017-05-11  Marek Polacek  <polacek@redhat.com>

	PR sanitizer/80536
	PR sanitizer/80386
	* cp-gimplify.c (cp_fold): Handle SAVE_EXPR.

	* tree.c (save_expr): Don't fold the expression.

	* c-c++-common/ubsan/pr80536.c: New test.
	* g++.dg/ubsan/pr80386.C: New test.

diff --git gcc/cp/cp-gimplify.c gcc/cp/cp-gimplify.c
index de62414..38a8ed4 100644
--- gcc/cp/cp-gimplify.c
+++ gcc/cp/cp-gimplify.c
@@ -2428,6 +2428,15 @@ cp_fold (tree x)
       x = fold (x);
       break;
 
+    case SAVE_EXPR:
+      /* A SAVE_EXPR might contain e.g. (0 * i) + (0 * j), which, after
+	 folding, evaluates to an invariant.  In that case no need to wrap
+	 this folded tree with a SAVE_EXPR.  */
+      r = cp_fold (TREE_OPERAND (x, 0));
+      if (tree_invariant_p (r))
+	x = r;
+      break;
+
     default:
       return org_x;
     }
diff --git gcc/testsuite/c-c++-common/ubsan/pr80536.c gcc/testsuite/c-c++-common/ubsan/pr80536.c
index e69de29..23913ad 100644
--- gcc/testsuite/c-c++-common/ubsan/pr80536.c
+++ gcc/testsuite/c-c++-common/ubsan/pr80536.c
@@ -0,0 +1,9 @@
+/* PR sanitizer/80536 */
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=undefined" } */
+
+int
+foo (int i)
+{
+  return ((i * (unsigned long long) (-0 + 1UL)) * 2) % 1;
+}
diff --git gcc/testsuite/g++.dg/ubsan/pr80386.C gcc/testsuite/g++.dg/ubsan/pr80386.C
index e69de29..60122da 100644
--- gcc/testsuite/g++.dg/ubsan/pr80386.C
+++ gcc/testsuite/g++.dg/ubsan/pr80386.C
@@ -0,0 +1,13 @@
+// PR sanitizer/80386
+// { dg-do run }
+// { dg-options "-fsanitize=undefined -fno-sanitize-recover" }
+
+static unsigned long long int i = 13996271126042720493ULL;
+
+int
+main ()
+{
+  int r = (((2921 + 0) - short(i)) + 0x7fffffff) >> 0;
+  asm volatile ("" : "+g" (r));
+  return 0;
+}
diff --git gcc/tree.c gcc/tree.c
index b76b521..7506725 100644
--- gcc/tree.c
+++ gcc/tree.c
@@ -3337,7 +3337,6 @@ tree_invariant_p (tree t)
 tree
 save_expr (tree expr)
 {
-  tree t = fold (expr);
   tree inner;
 
   /* If the tree evaluates to a constant, then we don't want to hide that
@@ -3345,33 +3344,32 @@ save_expr (tree expr)
      However, a read-only object that has side effects cannot be bypassed.
      Since it is no problem to reevaluate literals, we just return the
      literal node.  */
-  inner = skip_simple_arithmetic (t);
+  inner = skip_simple_arithmetic (expr);
   if (TREE_CODE (inner) == ERROR_MARK)
     return inner;
 
   if (tree_invariant_p_1 (inner))
-    return t;
+    return expr;
 
   /* If INNER contains a PLACEHOLDER_EXPR, we must evaluate it each time, since
      it means that the size or offset of some field of an object depends on
      the value within another field.
 
-     Note that it must not be the case that T contains both a PLACEHOLDER_EXPR
+     Note that it must not be the case that EXPR contains both a PLACEHOLDER_EXPR
      and some variable since it would then need to be both evaluated once and
      evaluated more than once.  Front-ends must assure this case cannot
      happen by surrounding any such subexpressions in their own SAVE_EXPR
      and forcing evaluation at the proper time.  */
   if (contains_placeholder_p (inner))
-    return t;
+    return expr;
 
-  t = build1 (SAVE_EXPR, TREE_TYPE (expr), t);
-  SET_EXPR_LOCATION (t, EXPR_LOCATION (expr));
+  expr = build1_loc (EXPR_LOCATION (expr), SAVE_EXPR, TREE_TYPE (expr), expr);
 
   /* This expression might be placed ahead of a jump to ensure that the
      value was computed on both sides of the jump.  So make sure it isn't
      eliminated as dead.  */
-  TREE_SIDE_EFFECTS (t) = 1;
-  return t;
+  TREE_SIDE_EFFECTS (expr) = 1;
+  return expr;
 }
 
 /* Look inside EXPR into any simple arithmetic operations.  Return the

	Marek

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2017-05-16 18:41 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-05-11 13:54 [PATCH] Don't fold in save_expr (PR sanitizer/80536, PR sanitizer/80386) Marek Polacek
2017-05-12  7:39 ` Richard Biener
2017-05-12 10:10   ` Marek Polacek
2017-05-12 10:26     ` Richard Biener
2017-05-12 16:58     ` Joseph Myers
2017-05-16 18:45     ` Jason Merrill

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