re PR c++/83555 (Unnecessary null check when static_cast is used with references.)
authorJakub Jelinek <jakub@redhat.com>
Wed, 3 Jan 2018 20:37:41 +0000 (21:37 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Wed, 3 Jan 2018 20:37:41 +0000 (21:37 +0100)
PR c++/83555
* typeck.c (build_static_cast_1): For static casts to reference types,
call build_base_path with flag_delete_null_pointer_checks as nonnull
instead of always false.  When -fsanitize=null, call
ubsan_maybe_instrument_reference on the NULL reference INTEGER_CST.
* cp-gimplify.c (cp_genericize_r): Don't walk subtrees of UBSAN_NULL
call if the first argument is INTEGER_CST with REFERENCE_TYPE.

* g++.dg/opt/pr83555.C: New test.
* g++.dg/ubsan/pr83555.C: New test.

From-SVN: r256186

gcc/cp/ChangeLog
gcc/cp/cp-gimplify.c
gcc/cp/typeck.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/opt/pr83555.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ubsan/pr83555.C [new file with mode: 0644]

index 4f8f8394273fb037be2a018f78e316d499a35319..959f412ebf39fd1eb68865b57950d4d516843aac 100644 (file)
@@ -1,3 +1,13 @@
+2018-01-03  Jakub Jelinek  <jakub@redhat.com>
+
+       PR c++/83555
+       * typeck.c (build_static_cast_1): For static casts to reference types,
+       call build_base_path with flag_delete_null_pointer_checks as nonnull
+       instead of always false.  When -fsanitize=null, call
+       ubsan_maybe_instrument_reference on the NULL reference INTEGER_CST.
+       * cp-gimplify.c (cp_genericize_r): Don't walk subtrees of UBSAN_NULL
+       call if the first argument is INTEGER_CST with REFERENCE_TYPE.
+
 2018-01-03  Nathan Sidwell  <nathan@acm.org>
 
        PR c++/83667
index c090ee7fcbd79b0a7eaa2900689eb6cc8970862c..eda493a7bec11c922ef38dda2a7ba20647bc122e 100644 (file)
@@ -1506,6 +1506,12 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
              if (sanitize_flags_p (SANITIZE_VPTR) && !is_ctor)
                cp_ubsan_maybe_instrument_member_call (stmt);
            }
+         else if (fn == NULL_TREE
+                  && CALL_EXPR_IFN (stmt) == IFN_UBSAN_NULL
+                  && TREE_CODE (CALL_EXPR_ARG (stmt, 0)) == INTEGER_CST
+                  && (TREE_CODE (TREE_TYPE (CALL_EXPR_ARG (stmt, 0)))
+                      == REFERENCE_TYPE))
+           *walk_subtrees = 0;
        }
       break;
 
index ccbef1db35c7783c3da20a42364e48fb01b7bd35..76fd9302edfc4c510e97878d675105530fdd4887 100644 (file)
@@ -6943,8 +6943,11 @@ build_static_cast_1 (tree type, tree expr, bool c_cast_p,
        }
 
       /* Convert from "B*" to "D*".  This function will check that "B"
-        is not a virtual base of "D".  */
-      expr = build_base_path (MINUS_EXPR, expr, base, /*nonnull=*/false,
+        is not a virtual base of "D".  Even if we don't have a guarantee
+        that expr is NULL, if the static_cast is to a reference type,
+        it is UB if it would be NULL, so omit the non-NULL check.  */
+      expr = build_base_path (MINUS_EXPR, expr, base,
+                             /*nonnull=*/flag_delete_null_pointer_checks,
                              complain);
 
       /* Convert the pointer to a reference -- but then remember that
@@ -6955,7 +6958,18 @@ build_static_cast_1 (tree type, tree expr, bool c_cast_p,
          is a variable with the same type, the conversion would get folded
          away, leaving just the variable and causing lvalue_kind to give
          the wrong answer.  */
-      return convert_from_reference (rvalue (cp_fold_convert (type, expr)));
+      expr = cp_fold_convert (type, expr);
+
+      /* When -fsanitize=null, make sure to diagnose reference binding to
+        NULL even when the reference is converted to pointer later on.  */
+      if (sanitize_flags_p (SANITIZE_NULL)
+         && TREE_CODE (expr) == COND_EXPR
+         && TREE_OPERAND (expr, 2)
+         && TREE_CODE (TREE_OPERAND (expr, 2)) == INTEGER_CST
+         && TREE_TYPE (TREE_OPERAND (expr, 2)) == type)
+       ubsan_maybe_instrument_reference (&TREE_OPERAND (expr, 2));
+
+      return convert_from_reference (rvalue (expr));
     }
 
   /* "A glvalue of type cv1 T1 can be cast to type rvalue reference to
index 503a8397ab6ff08536674c7ed8531b572028be5e..7c4e6e9a3495631751b248074e5dc5984d0119df 100644 (file)
@@ -1,3 +1,9 @@
+2018-01-03  Jakub Jelinek  <jakub@redhat.com>
+
+       PR c++/83555
+       * g++.dg/opt/pr83555.C: New test.
+       * g++.dg/ubsan/pr83555.C: New test.
+
 2018-01-03  David Malcolm  <dmalcolm@redhat.com>
 
        PR c/82050
diff --git a/gcc/testsuite/g++.dg/opt/pr83555.C b/gcc/testsuite/g++.dg/opt/pr83555.C
new file mode 100644 (file)
index 0000000..c6f810d
--- /dev/null
@@ -0,0 +1,15 @@
+// PR c++/83555
+// { dg-do compile }
+// { dg-options "-O2 -fdump-tree-optimized -fdelete-null-pointer-checks" }
+
+struct A { int a; };
+struct B { int b; };
+struct C : A, B { int c; };
+
+C *
+foo (B *b)
+{
+  return &static_cast<C &>(*b);
+}
+
+// { dg-final { scan-tree-dump-not "if \\(b_\[0-9]*\\(D\\) .= 0" "optimized" } }
diff --git a/gcc/testsuite/g++.dg/ubsan/pr83555.C b/gcc/testsuite/g++.dg/ubsan/pr83555.C
new file mode 100644 (file)
index 0000000..6574923
--- /dev/null
@@ -0,0 +1,40 @@
+// PR c++/83555
+// { dg-do run }
+// { dg-options "-fsanitize=null" }
+// { dg-output ":25:\[^\n\r]*reference binding to null pointer of type 'struct C'" }
+
+struct A { int a; };
+struct B { int b; };
+struct C : A, B { int c; };
+
+__attribute__((noipa)) C *
+foo (B *b)
+{
+  return static_cast<C *>(b);
+}
+
+__attribute__((noipa)) C *
+bar (B *b)
+{
+  return &static_cast<C &>(*b);
+}
+
+__attribute__((noipa)) C *
+baz (B *b)
+{
+  return &static_cast<C &>(*b);
+}
+
+int
+main ()
+{
+  C c;
+  if (foo (static_cast<B *> (&c)) != &c)
+    __builtin_abort ();
+  if (foo (0))
+    __builtin_abort ();
+  if (bar (static_cast<B *> (&c)) != &c)
+    __builtin_abort ();
+  baz (0);
+  return 0;
+}