On Fri, Sep 13, 2013 at 08:01:36PM +0200, Marek Polacek wrote:
> 2013-09-13  Marek Polacek  <pola...@redhat.com>
> 
>       PR sanitizer/58413
> c-family/
>       * c-ubsan.c (ubsan_instrument_shift): Don't instrument
>       an expression if we can prove it is correct.
> 
> testsuite/
>       * c-c++-common/ubsan/shift-4.c: New test.
>       * c-c++-common/ubsan/shift-5.c: New test.
>       * gcc.dg/ubsan/c-shift-1.c: New test.
> 
> --- gcc/c-family/c-ubsan.c.mp3        2013-09-13 19:19:55.410925155 +0200
> +++ gcc/c-family/c-ubsan.c    2013-09-13 19:20:16.766006242 +0200
> @@ -104,6 +104,40 @@ ubsan_instrument_shift (location_t loc,
>    tree uprecm1 = build_int_cst (op1_utype, op0_prec - 1);
>    tree precm1 = build_int_cst (type1, op0_prec - 1);
>  
> +  /* If OP0 is of an unsigned type, we may prove that OP1 is not
> +     greater than UPRECM1 (and not negative); in that case we can
> +     bail out early.  */
> +  if (TYPE_UNSIGNED (type0)
> +      && TREE_CODE (op1) == INTEGER_CST
> +      && tree_int_cst_sgn (op1) != -1
> +      && !tree_int_cst_lt (uprecm1, op1))
> +    return NULL_TREE;
> +
> +  /* Even when OP0 is of a signed type, we may prove that there's
> +     nothing wrong with the shift if both operands are INTEGER_CSTs
> +     and wouldn't trigger UB.  We do this only for C.
> +     XXX Should we treat ANSI C specially wrt 1 << 31?  */
> +  if (TREE_CODE (op0) == INTEGER_CST
> +      && TREE_CODE (op1) == INTEGER_CST
> +      && !TYPE_UNSIGNED (type0)
> +      && tree_int_cst_sgn (op1) != -1
> +      && !tree_int_cst_lt (uprecm1, op1)
> +      && !c_dialect_cxx ())
> +    {
> +      /* If this is a right shift, we can return now.  */
> +      if (code == RSHIFT_EXPR)
> +        return NULL_TREE;
> +
> +      /* Otherwise see comment below.  */
> +      tree x = fold_convert_loc (loc, unsigned_type_for (type0), op0);
> +      x = fold_build2 (RSHIFT_EXPR, TREE_TYPE (x), x,
> +                    fold_build2 (MINUS_EXPR, TREE_TYPE (precm1), precm1,
> +                                 op1));
> +      if (integer_zerop (x))
> +        return NULL_TREE;
> +    }
> +
> +  /* Ok, we have to do the instrumentation.  */

I'd say the above is going to be a maintainance nightmare, with all the code
duplication.  And you are certainly going to miss cases that way,
e.g.
void
foo (void)
{
  int A[-2 / -1] = {};
}

I'd say instead of adding all this, you should just at the right spot insert
if (integer_zerop (t)) return NULL_TREE; or similar.

For shift instrumentation, I guess you could add
if (integer_zerop (t) && (tt == NULL_TREE || integer_zerop (tt)))
  return NULL_TREE;
right before:
  t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t);

> +/* PR sanitizer/58413 */
> +/* { dg-do run } */
> +/* { dg-options "-fsanitize=shift -w" } */
> +
> +int x = 7;
> +int
> +main (void)
> +{
> +  /* All of the following should pass.  */
> +  int A[128 >> 5] = {};
> +  int B[128 << 5] = {};
> +
> +  static int e =
> +    ((int)
> +     (0x00000000 | ((31 & ((1 << (4)) - 1)) << (((15) + 6) + 4)) |
> +      ((0) << ((15) + 6)) | ((0) << (15))));

This relies on int32plus, so needs to be /* { dg-do run { target int32plus } } 
*/
> --- gcc/testsuite/c-c++-common/ubsan/shift-5.c.mp3    2013-09-13 
> 18:25:06.195847144 +0200
> +++ gcc/testsuite/c-c++-common/ubsan/shift-5.c        2013-09-13 
> 19:16:38.990211229 +0200
> @@ -0,0 +1,21 @@
> +/* { dg-do compile} */
> +/* { dg-options "-fsanitize=shift -w" } */
> +/* { dg-shouldfail "ubsan" } */
> +
> +int x;
> +int
> +main (void)
> +{
> +  /* None of the following should pass.  */
> +  switch (x)
> +    {
> +    case 1 >> -1:    /* { dg-error "" } */
> +    case -1 >> -1:   /* { dg-error "" } */
> +    case 1 << -1:    /* { dg-error "" } */
> +    case -1 << -1:   /* { dg-error "" } */
> +    case -1 >> 200:  /* { dg-error "" } */
> +    case 1 << 200:   /* { dg-error "" } */

Can't you fill in the error you are expecting, or is the problem
that the wording is very different between C and C++?

        Jakub

Reply via email to