diff mbox

c++/60760 - arithmetic on null pointers should not be allowed in constant expressions

Message ID 573504DD.9040707@gmail.com
State New
Headers show

Commit Message

Martin Sebor May 12, 2016, 10:34 p.m. UTC
Attached is a resubmission of the patch for c++/60760 originally
submitted late in the 6.0 cycle along with a patch for c++/67376.
Since c++/60760 was not a regression, it was decided that it
would be safer to defer the fix until after the 6.1.0 release.

While retesting this patch I was happy to notice that it also
fixes another bug: c++/71091 - constexpr reference bound to a null
pointer dereference accepted.

Martin

Comments

Jason Merrill May 17, 2016, 7:44 p.m. UTC | #1
On 05/12/2016 06:34 PM, Martin Sebor wrote:
> Attached is a resubmission of the patch for c++/60760 originally
> submitted late in the 6.0 cycle along with a patch for c++/67376.
> Since c++/60760 was not a regression, it was decided that it
> would be safer to defer the fix until after the 6.1.0 release.
>
> While retesting this patch I was happy to notice that it also
> fixes another bug: c++/71091 - constexpr reference bound to a null
> pointer dereference accepted.

I'm not sure why we need to track nullptr_p through everything.  Can't 
we set *non_constant_p instead in the places where it's problematic, as 
in cxx_eval_binary_expression?

I understand that the complication comes because of needing to allow

constexpr int *p = &*(int*)0;

but I don't see how cxx_eval_component_reference could come up with a 
constant value for the referent of a null pointer, so we already reject

struct A { int i; };
constexpr A* p = nullptr;
constexpr int i = p->i;

In cxx_eval_indirect_ref, we could check !lval and reject the 
constant-expression at that point.

Jason
Martin Sebor May 31, 2016, 10:03 p.m. UTC | #2
On 05/17/2016 01:44 PM, Jason Merrill wrote:
> On 05/12/2016 06:34 PM, Martin Sebor wrote:
>> Attached is a resubmission of the patch for c++/60760 originally
>> submitted late in the 6.0 cycle along with a patch for c++/67376.
>> Since c++/60760 was not a regression, it was decided that it
>> would be safer to defer the fix until after the 6.1.0 release.
>>
>> While retesting this patch I was happy to notice that it also
>> fixes another bug: c++/71091 - constexpr reference bound to a null
>> pointer dereference accepted.
>
> I'm not sure why we need to track nullptr_p through everything.  Can't
> we set *non_constant_p instead in the places where it's problematic, as
> in cxx_eval_binary_expression?

That would have certainly been my preference over adding another
argument to a subset of the functions, making their declarations
inconsistent.  Unfortunately, I couldn't come up with a simpler
way to make it work.

FWIW, my first inclination was to make the nullptr_p flag part
of constexpr_ctx and move the other xxx_p arguments there, to
simplify the interface, until I noticed that all the functions
take a const constexpr_ctx*, preventing them to modify it.  It
seemed like too much of a change to fix a bug in stage 3, but
I'd be comfortable making it in stage 1 if you think it's
worthwhile (though my preference would be to do in a separate
commit, after fixing this bug).

>
> I understand that the complication comes because of needing to allow
>
> constexpr int *p = &*(int*)0;
>
> but I don't see how cxx_eval_component_reference could come up with a
> constant value for the referent of a null pointer, so we already reject
>
> struct A { int i; };
> constexpr A* p = nullptr;
> constexpr int i = p->i;
>
> In cxx_eval_indirect_ref, we could check !lval and reject the
> constant-expression at that point.

The new code in cxx_eval_component_reference diagnoses the following
problem that's not detected otherwise:

   struct S { const S *s; };

   constexpr S s = { 0 };

   constexpr const void *p = &s.s->s;

Martin
Jason Merrill June 1, 2016, 2:36 p.m. UTC | #3
On 05/31/2016 06:03 PM, Martin Sebor wrote:
> On 05/17/2016 01:44 PM, Jason Merrill wrote:
>> On 05/12/2016 06:34 PM, Martin Sebor wrote:
>>> Attached is a resubmission of the patch for c++/60760 originally
>>> submitted late in the 6.0 cycle along with a patch for c++/67376.
>>> Since c++/60760 was not a regression, it was decided that it
>>> would be safer to defer the fix until after the 6.1.0 release.
>>>
>>> While retesting this patch I was happy to notice that it also
>>> fixes another bug: c++/71091 - constexpr reference bound to a null
>>> pointer dereference accepted.
>>
>> I'm not sure why we need to track nullptr_p through everything.  Can't
>> we set *non_constant_p instead in the places where it's problematic, as
>> in cxx_eval_binary_expression?
>
> That would have certainly been my preference over adding another
> argument to a subset of the functions, making their declarations
> inconsistent.  Unfortunately, I couldn't come up with a simpler
> way to make it work.
>
> FWIW, my first inclination was to make the nullptr_p flag part
> of constexpr_ctx and move the other xxx_p arguments there, to
> simplify the interface, until I noticed that all the functions
> take a const constexpr_ctx*, preventing them to modify it.  It
> seemed like too much of a change to fix a bug in stage 3, but
> I'd be comfortable making it in stage 1 if you think it's
> worthwhile (though my preference would be to do in a separate
> commit, after fixing this bug).
>>
>> I understand that the complication comes because of needing to allow
>>
>> constexpr int *p = &*(int*)0;
>>
>> but I don't see how cxx_eval_component_reference could come up with a
>> constant value for the referent of a null pointer, so we already reject
>>
>> struct A { int i; };
>> constexpr A* p = nullptr;
>> constexpr int i = p->i;
>>
>> In cxx_eval_indirect_ref, we could check !lval and reject the
>> constant-expression at that point.
>
> The new code in cxx_eval_component_reference diagnoses the following
> problem that's not detected otherwise:
>
>   struct S { const S *s; };
>
>   constexpr S s = { 0 };
>
>   constexpr const void *p = &s.s->s;

Note that this falls under core issue 1530, which has not been resolved. 
  I believe that this is fine under the current wording; only "access" 
to a non-static data member is undefined, and "access" is defined as 
reading or writing.  There has been discussion in the context of UBsan 
about making this undefined, but that hasn't been decided yet.  But I'm 
OK with changing G++ to go in that direction.

cxx_eval_component_reference could check whether 'whole' is a null (or 
other invalid) lvalue; for that testcase it's an INDIRECT_REF of 0.

Jason
diff mbox

Patch

PR c++/60760 - arithmetic on null pointers should not be allowed
   in constant expressions
PR c++/71091 - constexpr reference bound to a null pointer dereference
   accepted

gcc/testsuite/ChangeLog:
2016-05-12  Martin Sebor  <msebor@redhat.com>

	PR c++/60760
	PR c++/71091
	* g++.dg/cpp0x/constexpr-nullptr-2.C: New test.
	* gcc/testsuite/g++.dg/ubsan/pr63956.C: Adjust.

gcc/cp/ChangeLog:
2016-05-12  Martin Sebor  <msebor@redhat.com>

	PR c++/60760
	PR c++/71091
	* constexpr.c (cxx_eval_call_expression): Add argument.
	(cxx_eval_unary_expression): Same.
	(cxx_eval_conditional_expression): Same.
	(cxx_eval_array_reference): Same.
	(cxx_fold_indirect_ref): Same.
	(cxx_eval_statement_list): Same.
	(cxx_eval_loop_expr): Same.
	(cxx_eval_binary_expression): Same.  Detect and reject invalid uses
	of null pointers.
	(cxx_eval_component_reference): Same.
	(cxx_eval_constant_expression): Same.
	(cxx_eval_indirect_ref):   Add argument. Detect invalid uses of null
	pointers without rejecting them here.
	(cxx_eval_outermost_constant_expr): Adjust.

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 6054d1a..3821ad0 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -918,7 +918,8 @@  struct constexpr_ctx {
 static GTY (()) hash_table<constexpr_call_hasher> *constexpr_call_table;

 static tree cxx_eval_constant_expression (const constexpr_ctx *, tree,
-					  bool, bool *, bool *, tree * = NULL);
+					  bool, bool *, bool *, bool * = NULL,
+					  tree * = NULL);

 /* Compute a hash value for a constexpr call representation.  */

@@ -1491,7 +1492,7 @@  cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 	  tree jump_target = NULL_TREE;
 	  cxx_eval_constant_expression (&ctx_with_save_exprs, body,
 					lval, non_constant_p, overflow_p,
-					&jump_target);
+					NULL, &jump_target);

 	  if (DECL_CONSTRUCTOR_P (fun))
 	    /* This can be null for a subobject constructor call, in
@@ -1716,20 +1717,21 @@  cxx_eval_unary_expression (const constexpr_ctx *ctx, tree t,
 static tree
 cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t,
 			    bool /*lval*/,
-			    bool *non_constant_p, bool *overflow_p)
+			    bool *non_constant_p, bool *overflow_p,
+			    bool *nullptr_p)
 {
   tree r = NULL_TREE;
   tree orig_lhs = TREE_OPERAND (t, 0);
   tree orig_rhs = TREE_OPERAND (t, 1);
   tree lhs, rhs;
   lhs = cxx_eval_constant_expression (ctx, orig_lhs, /*lval*/false,
-				      non_constant_p, overflow_p);
+				      non_constant_p, overflow_p, nullptr_p);
   /* Don't VERIFY_CONSTANT here, it's unnecessary and will break pointer
      subtraction.  */
   if (*non_constant_p)
     return t;
   rhs = cxx_eval_constant_expression (ctx, orig_rhs, /*lval*/false,
-				      non_constant_p, overflow_p);
+				      non_constant_p, overflow_p, nullptr_p);
   if (*non_constant_p)
     return t;

@@ -1751,6 +1753,15 @@  cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t,
 		   || null_member_pointer_value_p (rhs)))
 	r = constant_boolean_node (!is_code_eq, type);
     }
+  if (code == POINTER_PLUS_EXPR && !*non_constant_p
+      && tree_int_cst_equal (lhs, null_pointer_node))
+    {
+      if (!ctx->quiet)
+	error ("arithmetic involving null pointer %qE", lhs);
+      if (nullptr_p)
+	*nullptr_p = true;
+      return t;
+    }

   if (r == NULL_TREE)
     r = fold_binary_loc (loc, code, type, lhs, rhs);
@@ -1791,11 +1802,11 @@  cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t,
     return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2),
 					 lval,
 					 non_constant_p, overflow_p,
-					 jump_target);
+					 NULL, jump_target);
   return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
 				       lval,
 				       non_constant_p, overflow_p,
-				       jump_target);
+				       NULL, jump_target);
 }

 /* Returns less than, equal to, or greater than zero if KEY is found to be
@@ -2066,7 +2077,8 @@  cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
 static tree
 cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
 			      bool lval,
-			      bool *non_constant_p, bool *overflow_p)
+			      bool *non_constant_p, bool *overflow_p,
+			      bool *nullptr_p)
 {
   unsigned HOST_WIDE_INT i;
   tree field;
@@ -2075,7 +2087,14 @@  cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
   tree orig_whole = TREE_OPERAND (t, 0);
   tree whole = cxx_eval_constant_expression (ctx, orig_whole,
 					     lval,
-					     non_constant_p, overflow_p);
+					     non_constant_p, overflow_p,
+					     nullptr_p);
+  if (nullptr_p && *nullptr_p)
+    {
+      if (!ctx->quiet)
+	error ("%qE dereferences a null pointer", orig_whole);
+    }
+
   if (TREE_CODE (whole) == PTRMEM_CST)
     whole = cplus_expand_constant (whole);
   if (whole == orig_whole)
@@ -2807,7 +2826,8 @@  cxx_fold_indirect_ref (location_t loc, tree type, tree op0, bool *empty_base)
 static tree
 cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
 		       bool lval,
-		       bool *non_constant_p, bool *overflow_p)
+		       bool *non_constant_p, bool *overflow_p,
+		       bool *nullptr_p)
 {
   tree orig_op0 = TREE_OPERAND (t, 0);
   bool empty_base = false;
@@ -2832,6 +2852,9 @@  cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
       tree op0 = cxx_eval_constant_expression (ctx, orig_op0,
 					       /*lval*/false, non_constant_p,
 					       overflow_p);
+      if (nullptr_p && tree_int_cst_equal (op0, null_pointer_node))
+	*nullptr_p = true;
+
       /* Don't VERIFY_CONSTANT here.  */
       if (*non_constant_p)
 	return t;
@@ -3328,7 +3351,7 @@  cxx_eval_statement_list (const constexpr_ctx *ctx, tree t,
 	}
       r = cxx_eval_constant_expression (ctx, stmt, false,
 					non_constant_p, overflow_p,
-					jump_target);
+					NULL, jump_target);
       if (*non_constant_p)
 	break;
       if (returns (jump_target) || breaks (jump_target))
@@ -3360,7 +3383,8 @@  cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
       new_ctx.save_exprs = &save_exprs;

       cxx_eval_constant_expression (&new_ctx, body, /*lval*/false,
-				    non_constant_p, overflow_p, jump_target);
+				    non_constant_p, overflow_p, NULL,
+				    jump_target);

       /* Forget saved values of SAVE_EXPRs.  */
       for (hash_set<tree>::iterator iter = save_exprs.begin();
@@ -3457,15 +3481,20 @@  cxx_eval_pointer_plus_expression (const constexpr_ctx *ctx, tree t,
 }

 /* Attempt to reduce the expression T to a constant value.
-   On failure, issue diagnostic and return error_mark_node.  */
+   On failure, issue diagnostic and return error_mark_node.
+   Similar to NON_CONSTANT_P and OVERFLOW_P, when NULLPTR_P is non-null,
+   the object it points to is set to true when an invalid use of a null
+   pointer is detected.  */
 /* FIXME unify with c_fully_fold */
 /* FIXME overflow_p is too global */

 static tree
 cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 			      bool lval,
-			      bool *non_constant_p, bool *overflow_p,
-			      tree *jump_target)
+			      bool *non_constant_p,
+			      bool *overflow_p,
+			      bool *nullptr_p,
+			      tree *jump_target)
 {
   constexpr_ctx new_ctx;
   tree r = t;
@@ -3484,10 +3513,22 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	  if (!flag_permissive || ctx->quiet)
 	    *overflow_p = true;
 	}
+
+      if (TREE_CODE (t) == INTEGER_CST
+	  && TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE
+	  && !integer_zerop (t))
+	{
+	  if (!ctx->quiet)
+	    error ("null pointer arithmetic in %qE", t);
+	  if (nullptr_p)
+	    *nullptr_p = true;
+	}
+
       return t;
     }

-  switch (TREE_CODE (t))
+  tree_code tcode = TREE_CODE (t);
+  switch (tcode)
     {
     case RESULT_DECL:
       if (lval)
@@ -3576,7 +3617,8 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	  {
 	    init = cxx_eval_constant_expression (ctx, init,
 						 false,
-						 non_constant_p, overflow_p);
+						 non_constant_p, overflow_p,
+						 nullptr_p);
 	    /* Don't share a CONSTRUCTOR that might be changed.  */
 	    init = unshare_constructor (init);
 	    ctx->values->put (r, init);
@@ -3616,7 +3658,8 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	 initialization of a temporary.  */
       r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
 					false,
-					non_constant_p, overflow_p);
+					non_constant_p, overflow_p,
+					nullptr_p);
       if (!*non_constant_p)
 	/* Adjust the type of the result to the type of the temporary.  */
 	r = adjust_temp_type (TREE_TYPE (t), r);
@@ -3638,14 +3681,16 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     case SCOPE_REF:
       r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
 					lval,
-					non_constant_p, overflow_p);
+					non_constant_p, overflow_p,
+					nullptr_p);
       break;

     case RETURN_EXPR:
       if (TREE_OPERAND (t, 0) != NULL_TREE)
 	r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
 					  lval,
-					  non_constant_p, overflow_p);
+					  non_constant_p, overflow_p,
+					  nullptr_p);
       *jump_target = t;
       break;

@@ -3656,7 +3701,8 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       else
 	{
 	  r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), false,
-					    non_constant_p, overflow_p);
+					    non_constant_p, overflow_p,
+					    nullptr_p);
 	  ctx->values->put (t, r);
 	  if (ctx->save_exprs)
 	    ctx->save_exprs->add (t);
@@ -3673,18 +3719,18 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
 					lval,
 					non_constant_p, overflow_p,
-					jump_target);
+					nullptr_p, jump_target);
       break;

     case TRY_FINALLY_EXPR:
       r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval,
 					non_constant_p, overflow_p,
-					jump_target);
+					nullptr_p, jump_target);
       if (!*non_constant_p)
 	/* Also evaluate the cleanup.  */
 	cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), true,
 				      non_constant_p, overflow_p,
-				      jump_target);
+				      nullptr_p, jump_target);
       break;

       /* These differ from cxx_eval_unary_expression in that this doesn't
@@ -3693,7 +3739,7 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     case MEM_REF:
     case INDIRECT_REF:
       r = cxx_eval_indirect_ref (ctx, t, lval,
-				 non_constant_p, overflow_p);
+				 non_constant_p, overflow_p, nullptr_p);
       break;

     case ADDR_EXPR:
@@ -3701,7 +3747,8 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	tree oldop = TREE_OPERAND (t, 0);
 	tree op = cxx_eval_constant_expression (ctx, oldop,
 						/*lval*/true,
-						non_constant_p, overflow_p);
+						non_constant_p, overflow_p,
+						nullptr_p);
 	/* Don't VERIFY_CONSTANT here.  */
 	if (*non_constant_p)
 	  return t;
@@ -3744,19 +3791,19 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	    || TREE_CODE (op1) == EMPTY_CLASS_EXPR)
 	  r = cxx_eval_constant_expression (ctx, op0,
 					    lval, non_constant_p, overflow_p,
-					    jump_target);
+					    nullptr_p, jump_target);
 	else
 	  {
 	    /* Check that the LHS is constant and then discard it.  */
 	    cxx_eval_constant_expression (ctx, op0,
 					  true, non_constant_p, overflow_p,
-					  jump_target);
+					  nullptr_p, jump_target);
 	    if (*non_constant_p)
 	      return t;
 	    op1 = TREE_OPERAND (t, 1);
 	    r = cxx_eval_constant_expression (ctx, op1,
 					      lval, non_constant_p, overflow_p,
-					      jump_target);
+					      nullptr_p, jump_target);
 	  }
       }
       break;
@@ -3807,7 +3854,7 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     case RANGE_EXPR:
     case COMPLEX_EXPR:
       r = cxx_eval_binary_expression (ctx, t, lval,
-				      non_constant_p, overflow_p);
+				      non_constant_p, overflow_p, nullptr_p);
       break;

       /* fold can introduce non-IF versions of these; still treat them as
@@ -3845,7 +3892,7 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	  return t;
 	}
       r = cxx_eval_component_reference (ctx, t, lval,
-					non_constant_p, overflow_p);
+					non_constant_p, overflow_p, nullptr_p);
       break;

     case BIT_FIELD_REF:
@@ -3898,12 +3945,12 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     case NOP_EXPR:
     case UNARY_PLUS_EXPR:
       {
-	enum tree_code tcode = TREE_CODE (t);
 	tree oldop = TREE_OPERAND (t, 0);

 	tree op = cxx_eval_constant_expression (ctx, oldop,
 						lval,
-						non_constant_p, overflow_p);
+						non_constant_p, overflow_p,
+						nullptr_p);
 	if (*non_constant_p)
 	  return t;
 	tree type = TREE_TYPE (t);
@@ -3924,15 +3971,28 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 		return t;
 	      }
 	  }
-	if (POINTER_TYPE_P (type)
-	    && TREE_CODE (op) == INTEGER_CST
-	    && !integer_zerop (op))
-	  {
-	    if (!ctx->quiet)
-	      error_at (EXPR_LOC_OR_LOC (t, input_location),
-			"reinterpret_cast from integer to pointer");
-	    *non_constant_p = true;
-	    return t;
+	if (POINTER_TYPE_P (type) && TREE_CODE (op) == INTEGER_CST)
+	  {
+	    const char *msg = NULL;
+	    if (integer_zerop (op))
+	      {
+		if (nullptr_p)
+		  *nullptr_p = true;
+		if (!same_type_ignoring_top_level_qualifiers_p (type,
+								TREE_TYPE (op)))
+		  msg = "invalid conversion involving a null pointer";
+	      }
+	    else
+	      msg = "reinterpret_cast from integer to pointer";
+
+	    if (msg)
+	      {
+		if (!ctx->quiet)
+		  error_at (EXPR_LOC_OR_LOC (t, input_location), msg);
+
+		*non_constant_p = true;
+		return t;
+	      }
 	  }
 	if (op == oldop && tcode != UNARY_PLUS_EXPR)
 	  /* We didn't fold at the top so we could check for ptr-int
@@ -3965,7 +4025,7 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       return cxx_eval_constant_expression (ctx, BIND_EXPR_BODY (t),
 					   lval,
 					   non_constant_p, overflow_p,
-					   jump_target);
+					   nullptr_p, jump_target);

     case PREINCREMENT_EXPR:
     case POSTINCREMENT_EXPR:
@@ -4014,7 +4074,7 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	  tree ctor = lval ? ctx->object : ctx->ctor;
 	  return cxx_eval_constant_expression
 	    (ctx, ctor, lval,
-	     non_constant_p, overflow_p);
+	     non_constant_p, overflow_p, nullptr_p);
 	}
       break;

@@ -4132,8 +4192,9 @@  cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
 	r = TARGET_EXPR_INITIAL (r);
     }

-  r = cxx_eval_constant_expression (&ctx, r,
-				    false, &non_constant_p, &overflow_p);
+  bool nullptr_p = false;
+  r = cxx_eval_constant_expression (&ctx, r, false,
+				    &non_constant_p, &overflow_p, &nullptr_p);

   verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p);

diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-2.C
new file mode 100644
index 0000000..9ee1316
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-2.C
@@ -0,0 +1,191 @@ 
+// PR c++/60760 - arithmetic on null pointers should not be allowed
+//     in constant expressions
+// PR c++/71091 - constexpr reference bound to a null pointer dereference
+//     accepted
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-Wno-pointer-arith" }
+
+// Generate a null poiinter.
+constexpr int* null () { return 0; }
+
+// Test case from comment #0 in c++/60760.
+namespace PR_60760_comment_0 {
+
+constexpr int* ptr = nullptr;
+constexpr int* ptr2 = ptr + 1;		  // { dg-error "null pointer|not a constant" }
+
+}
+
+// Test case from comment #1 in c++/60760.
+namespace PR_60760_comment_1 {
+
+constexpr int* ptr = nullptr;
+constexpr int x = 0;
+constexpr int* ptr2 = ptr + x;   // Adding zero is valid.
+constexpr int* ptr3 = ptr - x;   // As is subtracting zero.
+
+}
+
+// Test case from c++/71091.
+namespace PR_71091 {
+
+constexpr int *p = 0;
+constexpr int &r = *p;			  // { dg-error "null pointer" }
+
+}
+
+// Other testr cases.
+namespace C {
+
+struct S { int a, b[1]; } s;
+
+constexpr S *p0 = &s;
+constexpr S *p1 = nullptr;
+
+constexpr int *r0 = p1->b;		      // { dg-error "null pointer|constant expression" }
+
+}
+
+namespace D {
+
+struct A { int i; const A *pa1; const A *pa0; };
+
+constexpr A a1 = { 0, 0, 0  };
+constexpr A a2 = { 1, &a1, 0 };
+
+constexpr const A *pa2 = &a2;
+constexpr int i0 = pa2->i;
+constexpr int i1 = pa2->pa1->i;
+constexpr int i2 = pa2->pa1->pa0->i;	    // { dg-error "null pointer|not a constant" }
+
+constexpr const A *pa3 = &*pa2->pa1->pa0;
+constexpr const A *pa4 = pa2->pa1->pa0 + 1;     // { dg-error "null pointer|not a constant" }
+
+constexpr const int *pi0 = &pa2->pa1->pa0->i;   // { dg-error "null pointer|not a constant" }
+
+constexpr const A *pa5 = 0;
+constexpr const int *pi1 = &pa5->i;	     // { dg-error "null pointer|not a constant" }
+
+}
+
+
+namespace SimpleTests {
+
+constexpr int* p0 = nullptr;
+constexpr int* q0 = p0;
+constexpr int* r0 = null ();
+
+// Adding or subtracting zero from a null pointer is valid in C++.
+constexpr int* p1 = p0 + 0;
+constexpr int* p2 = p0 - 0;
+constexpr int* p3 = 0 + p0;
+
+// While the text of the C++ standard still doesn't allow it, CWG
+// issue 232 implies that dererencing a null pointer is intended
+// to be permitted in contexts where the result isn't evaluated.
+// For compatibility with C that should at a minimum include
+// expressions like &*p that are valid there.
+constexpr int* p4 = &*p0;
+constexpr int* p5 = p0 + 1;       // { dg-error "null pointer|not a constant" }
+constexpr int* p6 = 1 + p0;       // { dg-error "null pointer|not a constant" }
+constexpr int* p7 = p0 - 1;       // { dg-error "null pointer|not a constant" }
+constexpr int* p8 = &p0 [0];
+constexpr int* p9 = &0 [p0];
+
+constexpr int* p10 = null () + 2; // { dg-error "null pointer|not a constant" }
+constexpr int* p11 = 3 + null (); // { dg-error "null pointer|not a constant" }
+constexpr int* p12 = null () - 4; // { dg-error "null pointer|not a constant" }
+constexpr int* p13 = &null ()[4]; // { dg-error "null pointer|not a constant" }
+constexpr int* p14 = &3[null ()]; // { dg-error "null pointer|not a constant" }
+
+constexpr int* q1 = q0 + 0;
+constexpr int* q2 = q0 - 0;
+constexpr int* q3 = q0 + 1;       // { dg-error "null pointer|not a constant" }
+constexpr int* q4 = q0 + 2;       // { dg-error "null pointer|not a constant" }
+constexpr int* q5 = &q0 [0];
+
+// Subtracting null pointers from one another is valid.
+constexpr int i0 = p0 - (int*)0;
+constexpr int i1 = p0 - static_cast<int*>(0);
+constexpr int i2 = p0 - (int*)nullptr;
+constexpr int i3 = p0 - static_cast<int*>(nullptr);
+constexpr int i4 = p0 - p0;
+constexpr int i5 = p0 - q0;
+constexpr int i6 = p0 - r0;
+constexpr int i7 = (int*)0 - p0;
+constexpr int i8 = static_cast<int*>(0) - p0;
+constexpr int i9 = (int*)nullptr - p0;
+constexpr int i10 = static_cast<int*>(nullptr) - p0;
+constexpr int i11 = q0 - p0;
+constexpr int i12 = r0 - p0;
+
+}
+
+namespace IndirectTests {
+
+struct S { int i, j; struct SA { struct SB { int *pi; } sb; } sa; };
+
+constexpr S* ps = (S*)0;
+
+// Comparing null pointers is valid.
+constexpr bool b0 = ps == ps;
+constexpr bool b1 = ps != ps;
+constexpr bool b2 = ps <  ps;
+constexpr bool b3 = ps <= ps;
+constexpr bool b4 = ps >  ps;
+constexpr bool b5 = ps >= ps;
+
+constexpr bool b6 = ps == (S*)0;
+constexpr bool b7 = ps != (S*)0;
+constexpr bool b8 = ps <  (S*)0;
+constexpr bool b9 = ps <= (S*)0;
+constexpr bool b10 = ps >  (S*)0;
+constexpr bool b11 = ps >= (S*)0;
+
+constexpr S* ps1 = ps;
+constexpr S* ps2 = ps1;
+
+// The following aren't diagnosed due to a bug.
+// constexpr int* pi0 = &((S*)0)->i;
+// constexpr int* pi1 = &((S*)nullptr)->i;
+
+constexpr int* pj0 = &((S*)0)->j;	// { dg-error "null pointer|not a constant" }
+constexpr int* pj1 = &((S*)nullptr)->j;  // { dg-error "null pointer|not a constant" }
+
+constexpr int* psi = &ps->i;	    // { dg-error "null pointer|not a constant" }
+constexpr int* psj = &ps->j;	    // { dg-error "null pointer|not a constant" }
+
+constexpr int* ps1i = &ps1->i;	  // { dg-error "null pointer|not a constant" }
+constexpr int* ps2i = &ps1->i;	  // { dg-error "null pointer|not a constant" }
+
+constexpr int* ps1j = &ps1->j;	  // { dg-error "null pointer|not a constant" }
+constexpr int* ps2j = &ps1->j;	  // { dg-error "null pointer|not a constant" }
+
+}
+
+namespace FunctionTests {
+
+typedef void Func ();
+
+// Arithmetic on member function pointers is diagnosed with -Wpointer-arith.
+// With constexpr, only zero may be added or subtracted.
+constexpr Func *pf0 = 0;
+constexpr Func *pf1 = pf0 + 0;  // triggers -Wpointer-arith
+constexpr Func *pf2 = pf0 - 0;  // triggers -Wpointer-arith
+constexpr Func *pf3 = 0 + pf0;  // triggers -Wpointer-arith
+constexpr Func *pf4 = pf0 + 1;  // { dg-error "null pointer|not a constant" }
+constexpr Func *pf5 = 2 + pf0;  // { dg-error "null pointer|not a constant" }
+constexpr Func *pf6 = pf0 - 3;  // { dg-error "null pointer|not a constant" }
+
+struct S;
+typedef void (S::*MemFuncPtr)();
+
+// Arithmetic on member function pointers is rejected with a hard error.
+constexpr MemFuncPtr pmf0 = nullptr;
+constexpr MemFuncPtr pmf1 = pmf0 + 0;   // { dg-error "invalid operands" }
+constexpr MemFuncPtr pmf2 = 0 + pmf0;   // { dg-error "invalid operands" }
+constexpr MemFuncPtr pmf3 = pmf0 + 1;   // { dg-error "invalid operands" }
+constexpr MemFuncPtr pmf4 = 1 + pmf0;   // { dg-error "invalid operands" }
+constexpr MemFuncPtr pmf5 = pmf0 - 1;   // { dg-error "invalid operands" }
+
+}
diff --git a/gcc/testsuite/g++.dg/ubsan/pr63956.C b/gcc/testsuite/g++.dg/ubsan/pr63956.C
index 25db8a4..ac01fa4 100644
--- a/gcc/testsuite/g++.dg/ubsan/pr63956.C
+++ b/gcc/testsuite/g++.dg/ubsan/pr63956.C
@@ -92,7 +92,7 @@  constexpr int
 fn6 (const int &a, int b)
 {
   if (b != 2)
-    b = a;  // { dg-error "is not a constant expression" }
+    b = a;
   return b;
 }

@@ -106,7 +106,7 @@  fn7 (const int *a, int b)

 constexpr int n1 = 7;
 constexpr int n2 = fn7 (&n1, 5);
-constexpr int n3 = fn7 ((const int *) 0, 8);
+constexpr int n3 = fn7 ((const int *) 0, 8);  // { dg-error "null pointer" }

 constexpr int
 fn8 (int i)