From 8bada5cd4e51feb60e1e887ecf0bda3288949af4 Mon Sep 17 00:00:00 2001 From: Martin Sebor Date: Sat, 30 Jul 2016 22:36:56 +0000 Subject: [PATCH] PR c++/60760 - arithmetic on null pointers should not be allowed in constant PR c++/60760 - arithmetic on null pointers should not be allowed in constant PR c++/71091 - constexpr reference bound to a null pointer dereference gcc/cp/ChangeLog: PR c++/60760 PR c++/71091 * constexpr.c (cxx_eval_binary_expression): Reject invalid expressions involving null pointers. (cxx_eval_component_reference): Reject null pointer dereferences. (cxx_eval_indirect_ref): Reject indirecting through null pointers. (cxx_eval_constant_expression): Reject invalid expressions involving null pointers. gcc/testsuite/ChangeLog: PR c++/60760 PR c++/71091 * g++.dg/cpp0x/constexpr-cast.C: New test. * g++.dg/cpp0x/constexpr-nullptr-2.C: New test. * g++.dg/cpp1y/constexpr-sfinae.C: Correct. * g++.dg/ubsan/pr63956.C: Correct. From-SVN: r238909 --- gcc/cp/ChangeLog | 11 + gcc/cp/constexpr.c | 84 ++++- gcc/testsuite/ChangeLog | 9 + gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C | 24 ++ .../g++.dg/cpp0x/constexpr-nullptr-2.C | 303 ++++++++++++++++++ gcc/testsuite/g++.dg/cpp1y/constexpr-sfinae.C | 18 +- gcc/testsuite/g++.dg/ubsan/pr63956.C | 4 +- 7 files changed, 435 insertions(+), 18 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-2.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index db6e1eb7730..1037208d5bd 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,14 @@ +2016-07-30 Martin Sebor + + PR c++/60760 + PR c++/71091 + * constexpr.c (cxx_eval_binary_expression): Reject invalid expressions + involving null pointers. + (cxx_eval_component_reference): Reject null pointer dereferences. + (cxx_eval_indirect_ref): Reject indirecting through null pointers. + (cxx_eval_constant_expression): Reject invalid expressions involving + null pointers. + 2016-07-29 Marek Polacek PR c/71926 diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 58716892f71..8bda97373b1 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -1848,6 +1848,13 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t, else if (TREE_CODE (rhs) == PTRMEM_CST) rhs = cplus_expand_constant (rhs); } + if (code == POINTER_PLUS_EXPR && !*non_constant_p + && integer_zerop (lhs) && !integer_zerop (rhs)) + { + if (!ctx->quiet) + error ("arithmetic involving a null pointer in %qE", lhs); + return t; + } if (r == NULL_TREE) r = fold_binary_loc (loc, code, type, lhs, rhs); @@ -2195,6 +2202,11 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t, tree whole = cxx_eval_constant_expression (ctx, orig_whole, lval, non_constant_p, overflow_p); + if (TREE_CODE (whole) == INDIRECT_REF + && integer_zerop (TREE_OPERAND (whole, 0)) + && !ctx->quiet) + error ("dereferencing a null pointer in %qE", orig_whole); + if (TREE_CODE (whole) == PTRMEM_CST) whole = cplus_expand_constant (whole); if (whole == orig_whole) @@ -2955,6 +2967,14 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t, if (*non_constant_p) return t; + if (!lval && integer_zerop (op0)) + { + if (!ctx->quiet) + error ("dereferencing a null pointer"); + *non_constant_p = true; + return t; + } + r = cxx_fold_indirect_ref (EXPR_LOCATION (t), TREE_TYPE (t), op0, &empty_base); if (r == NULL_TREE) @@ -3614,10 +3634,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 ("value %qE of type %qT is not a constant expression", + t, TREE_TYPE (t)); + *non_constant_p = true; + } + return t; } - switch (TREE_CODE (t)) + tree_code tcode = TREE_CODE (t); + switch (tcode) { case RESULT_DECL: if (lval) @@ -4041,7 +4073,6 @@ 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, @@ -4067,15 +4098,48 @@ 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 (POINTER_TYPE_P (type) && TREE_CODE (op) == INTEGER_CST) { - 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 (integer_zerop (op)) + { + if (TREE_CODE (type) == REFERENCE_TYPE) + { + if (!ctx->quiet) + error_at (EXPR_LOC_OR_LOC (t, input_location), + "dereferencing a null pointer"); + *non_constant_p = true; + return t; + } + else if (TREE_CODE (TREE_TYPE (op)) == POINTER_TYPE) + { + tree from = TREE_TYPE (op); + + if (!can_convert (type, from, tf_none)) + { + if (!ctx->quiet) + error_at (EXPR_LOC_OR_LOC (t, input_location), + "conversion of %qT null pointer to %qT " + "is not a constant expression", + from, type); + *non_constant_p = true; + return t; + } + } + } + else + { + /* This detects for example: + reinterpret_cast(sizeof 0) + */ + if (!ctx->quiet) + error_at (EXPR_LOC_OR_LOC (t, input_location), + "%(%E)%> is not " + "a constant-expression", + type, op); + *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 diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 8a2d5c8850b..a73a39defc6 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,12 @@ +2016-07-30 Martin Sebor + + PR c++/60760 + PR c++/71091 + * g++.dg/cpp0x/constexpr-cast.C: New test. + * g++.dg/cpp0x/constexpr-nullptr-2.C: New test. + * g++.dg/cpp1y/constexpr-sfinae.C: Correct. + * g++.dg/ubsan/pr63956.C: Correct. + 2016-07-30 Michael Meissner * gcc.target/powerpc/vec-extract-2.c: New tests for vec_extract of diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C new file mode 100644 index 00000000000..8e11193be53 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C @@ -0,0 +1,24 @@ +// Test to verify that evaluating reinterpret_cast is diagnosed in +// constant expressions. +// { dg-do compile { target c++11 } } + +int i; + +// The following is accepted due to bug 49171. +constexpr void *q = reinterpret_cast(&i); // { dg-error "" "bug c++/49171" { xfail *-*-*-* } } + +constexpr void *r0 = reinterpret_cast(1); // { dg-error "not a constant expression" } +constexpr void *r1 = reinterpret_cast(sizeof 'x'); // { dg-error ".reinterpret_cast\\(1ul\\). is not a constant-expression" } + +template +constexpr bool f () +{ +#if __cplusplus > 201103L + T *p = reinterpret_cast(sizeof (T)); + return p; +#else + return *reinterpret_cast(sizeof (T)); +#endif +} + +constexpr bool b = f(); // { dg-error "not a constant expression" } 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 00000000000..aeea87c2ae7 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-2.C @@ -0,0 +1,303 @@ +// 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 zero = 0; +constexpr int* ptr2 = ptr + zero; // Adding zero is valid. +constexpr int* ptr3 = ptr - zero; // As is subtracting zero. + +} + +// Test case from c++/71091. +namespace PR_71091 { + +constexpr int *p = 0; +constexpr const int &r = *p; // { dg-error "dereferencing a null pointer" } + +} + +// Other test 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" } + +// Adding and subtracting zero from and to a null pointer is valid. +constexpr S* r1 = p1 + 0; +constexpr S* r2 = r1 - 0; + +constexpr int zero = 0; + +constexpr S* r3 = r2 + zero; +constexpr S* r4 = r3 - zero; + +static_assert (r4 == nullptr, "r4 == nullptr"); + +constexpr const S *pcs = p0; +constexpr int d1 = pcs - p0; +constexpr int d2 = p0 - pcs; + +constexpr bool b = !p1 && !pcs; +} + +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 (); + +// Conversion to cv-qualified void* is valid. +constexpr void* pv0 = p0; +constexpr const void* pv1 = p0; +constexpr volatile void* pv2 = p0; +constexpr const volatile void* pv3 = p0; +constexpr void* pv4 = static_cast(p0); +constexpr const void* pv5 = static_cast(p0); + +// The following should be rejected but isn't because of bug c++/49171 +// - [C++0x][constexpr] Constant expressions support reinterpret_cast +constexpr void* pv6 = reinterpret_cast(p0); // { dg-error "" "bug c++/49171" { xfail *-*-* } } + +// 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(0); +constexpr int i2 = p0 - (int*)nullptr; +constexpr int i3 = p0 - static_cast(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(0) - p0; +constexpr int i9 = (int*)nullptr - p0; +constexpr int i10 = static_cast(nullptr) - p0; +constexpr int i11 = q0 - p0; +constexpr int i12 = r0 - p0; + +} + +namespace IncompleteTypeTests { + +// The type must be complete. +struct X; +constexpr X *px0 = nullptr; +constexpr X *px1 = px0 + 0; // { dg-error "invalid use of incomplete type" } +constexpr X *px2 = px0 - 0; // { dg-error "invalid use of incomplete type" } +constexpr X *px3 = px0 - px0; // { dg-error "invalid use of incomplete type" } + +constexpr void *pv0 = px0; +constexpr void *pv1 = pv0; +constexpr const void *pv2 = pv0; +constexpr void *pv3 = pv2; // { dg-error "invalid conversion|not a constant expression" } +constexpr const void *pv4 = pv2; + +constexpr X *px4 = pv0; // { dg-error "invalid conversion|not a constant expression" } + +} + +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 "not a constant expression" } +constexpr int* pj1 = &((S*)nullptr)->j; // { dg-error "not a constant expression" } + +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 BaseAndDerivedTests { + +struct A { }; +struct B: A { }; +struct C: B { }; +struct D: B, C { }; // { dg-warning "inaccessible" } + +constexpr D *pd0 = 0; +constexpr C *pc0 = 0; +constexpr B *pb0 = 0; + +constexpr A *pa0 = pb0; +constexpr A *pa1 = static_cast(pb0); +constexpr A *pa2 = pc0; +constexpr A *pa3 = pd0; // { dg-error "ambiguous base" } +constexpr A *pa4 = static_cast(pd0); // { dg-error "ambiguous base" } + +constexpr B *pb1 = pa0; // { dg-error "invalid conversion|not a constant expression" } +constexpr B *pb2 = static_cast(pa0); // { dg-error "not a constant expression" } + +constexpr C *pc1 = pa0; // { dg-error "invalid conversion|not a constant expression" } +constexpr D *pd1 = pa0; // { dg-error "ambiguous base|invalid conversion" } + +struct E: private A { }; + +constexpr E *pe0 = 0; +constexpr A *pa5 = pe0; // { dg-error "inaccessible base of" } + +struct VA { virtual ~VA (); }; +struct VB: virtual VA { }; +struct VC: virtual VA { }; +struct VD: VB, VC { }; + +constexpr VD *pvd0 = 0; +constexpr VC *pvc0 = 0; +constexpr VB *pvb0 = 0; + +constexpr VA *pva0 = pvb0; +constexpr VA *pva1 = pvc0; +constexpr VA *pva2 = pvd0; + +constexpr VB *pvb1 = pva0; // { dg-error "invalid conversion|cannot convert from pointer to base class" } + +} + +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" } + +} + +namespace ConversionTest { + +struct A { + int *p; +}; + +constexpr const int* f (const int *p) { return p; } + +void f () +{ + static_assert (!f (0), "f (a.p)"); + static_assert (!f (nullptr), "f (a.p)"); + + constexpr A a = A (); + + static_assert (!f (a.p), "f (a.p)"); +} + +} diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-sfinae.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-sfinae.C index a83d7f4e1de..4a7deb8e826 100644 --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-sfinae.C +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-sfinae.C @@ -90,22 +90,28 @@ namespace NullPointerArithmetic { constexpr int i = 0; constexpr const int* a[] = { 0, &i }; -// Well-defined core constant expressoons involving null pointers. +// Well-defined core constant expressions involving null pointers. constexpr __PTRDIFF_TYPE__ d00 = a [0] - a [0]; constexpr __PTRDIFF_TYPE__ d11 = a [1] - a [1]; -// Undefined core constant expressoons involving null pointers. +// Undefined core constant expressions involving null pointers. // constexpr __PTRDIFF_TYPE__ d01 = a [0] - a [1]; // constexpr __PTRDIFF_TYPE__ d10 = a [1] - a [0]; -constexpr bool nullptr_sub_0 (int i, int j) { return 1 + a [i != 0] - a [j]; } +// Valid when i == j. +constexpr bool +nullptr_sub_0 (bool i, bool j) { return 1 + a [!i] - a [!j]; } -constexpr bool nullptr_sub_1 (int i, int j) { return 1 + a [i == 0] - a [j]; } +// Valid when i != j. +constexpr bool +nullptr_sub_1 (bool i, bool j) { return 1 + a [i] - a [!j]; } -template +// Selected when I == 0. +template constexpr int f (int (*)[nullptr_sub_0 (I, 0)] = 0) { return 0; } -template +// Selected when I != 0. +template constexpr int f (int (*)[nullptr_sub_1 (I, 0)] = 0) { return 1; } constexpr int n0 = f<0>(); diff --git a/gcc/testsuite/g++.dg/ubsan/pr63956.C b/gcc/testsuite/g++.dg/ubsan/pr63956.C index 25db8a40e52..ac01fa4fdd2 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) -- 2.30.2