On Thu, May 11, 2017 at 3:49 PM, Marek Polacek <pola...@redhat.com> wrote: > 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?
Interesting - I tried this once (removing fold () from save_expr) but it failed miserably (don't remember the details). Maybe the cp/ part fixes it up. Did you include Ada in testing? Otherwise the tree.c change is ok. (yay, one less to go in the attempt to remove fold ()) Thanks! Richard. > 2017-05-11 Marek Polacek <pola...@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