Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
On 08/02/2016 12:34 AM, Thomas Schwinge wrote: Hi! On Wed, 6 Jul 2016 16:20:44 -0600, Martin Seborwrote: 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 [...] * g++.dg/cpp0x/constexpr-cast.C: New test. In x86_64 GNU/Linux testing, I see that one FAIL for the -m32 multilib: +FAIL: g++.dg/cpp0x/constexpr-cast.C -std=c++11 (test for errors, line 10) +FAIL: g++.dg/cpp0x/constexpr-cast.C -std=c++11 (test for errors, line 11) +PASS: g++.dg/cpp0x/constexpr-cast.C -std=c++11 (test for errors, line 24) +FAIL: g++.dg/cpp0x/constexpr-cast.C -std=c++11 (test for excess errors) +XFAIL: g++.dg/cpp0x/constexpr-cast.C -std=c++11 bug c++/49171 (test for errors, line 8) +FAIL: g++.dg/cpp0x/constexpr-cast.C -std=c++14 (test for errors, line 10) +FAIL: g++.dg/cpp0x/constexpr-cast.C -std=c++14 (test for errors, line 11) +PASS: g++.dg/cpp0x/constexpr-cast.C -std=c++14 (test for errors, line 24) +FAIL: g++.dg/cpp0x/constexpr-cast.C -std=c++14 (test for excess errors) +XFAIL: g++.dg/cpp0x/constexpr-cast.C -std=c++14 bug c++/49171 (test for errors, line 8) +UNSUPPORTED: g++.dg/cpp0x/constexpr-cast.C -std=c++98 [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:10:22: error: 'reinterpret_cast (1)' is not a constant-expression [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:11:22: error: 'reinterpret_cast (1u)' is not a constant-expression [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:24:26: in constexpr expansion of 'f()' [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:24:27: error: value '4u' of type 'int*' is not a constant expression For the -m64 multilib, it looks as follows (all PASSes): [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:10:47: error: value '1u' of type 'void*' is not a constant expression [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:11:22: error: 'reinterpret_cast (1ul)' is not a constant-expression [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:24:26: in constexpr expansion of 'f()' [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:24:27: error: value '4u' of type 'int*' is not a constant expression Thanks for pointing it out and for all the detail! I managed to run into at least two problems with this change: one in the test assuming that (void*)1 will appear in GCC diagnostics as 1ul, and another in GCC due to the inconsistent spelling of "constant expression." Some errors hyphenate the words, others don't, and depending on which one triggers a test that assumes one or the other will fail. Let me submit a patch for this and CC you on it. Martin For reference: --- /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 ();// { 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" } Grüße Thomas
Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
Hi! On Wed, 6 Jul 2016 16:20:44 -0600, Martin Seborwrote: > 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 > > [...] > * g++.dg/cpp0x/constexpr-cast.C: New test. In x86_64 GNU/Linux testing, I see that one FAIL for the -m32 multilib: +FAIL: g++.dg/cpp0x/constexpr-cast.C -std=c++11 (test for errors, line 10) +FAIL: g++.dg/cpp0x/constexpr-cast.C -std=c++11 (test for errors, line 11) +PASS: g++.dg/cpp0x/constexpr-cast.C -std=c++11 (test for errors, line 24) +FAIL: g++.dg/cpp0x/constexpr-cast.C -std=c++11 (test for excess errors) +XFAIL: g++.dg/cpp0x/constexpr-cast.C -std=c++11 bug c++/49171 (test for errors, line 8) +FAIL: g++.dg/cpp0x/constexpr-cast.C -std=c++14 (test for errors, line 10) +FAIL: g++.dg/cpp0x/constexpr-cast.C -std=c++14 (test for errors, line 11) +PASS: g++.dg/cpp0x/constexpr-cast.C -std=c++14 (test for errors, line 24) +FAIL: g++.dg/cpp0x/constexpr-cast.C -std=c++14 (test for excess errors) +XFAIL: g++.dg/cpp0x/constexpr-cast.C -std=c++14 bug c++/49171 (test for errors, line 8) +UNSUPPORTED: g++.dg/cpp0x/constexpr-cast.C -std=c++98 [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:10:22: error: 'reinterpret_cast (1)' is not a constant-expression [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:11:22: error: 'reinterpret_cast (1u)' is not a constant-expression [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:24:26: in constexpr expansion of 'f()' [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:24:27: error: value '4u' of type 'int*' is not a constant expression For the -m64 multilib, it looks as follows (all PASSes): [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:10:47: error: value '1u' of type 'void*' is not a constant expression [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:11:22: error: 'reinterpret_cast (1ul)' is not a constant-expression [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:24:26: in constexpr expansion of 'f()' [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:24:27: error: value '4u' of type 'int*' is not a constant expression For reference: > --- /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 ();// { 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" } Grüße Thomas signature.asc Description: PGP signature
Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
On Wed, Jul 20, 2016 at 2:15 PM, Martin Seborwrote: > On 07/20/2016 07:52 AM, Jason Merrill wrote: >> >> On Mon, Jul 18, 2016 at 6:15 PM, Martin Sebor wrote: >>> >>> On 07/18/2016 11:51 AM, Jason Merrill wrote: On 07/06/2016 06:20 PM, Martin Sebor wrote: > > > @@ -2911,6 +2923,14 @@ cxx_eval_indirect_ref (const constexpr_ctx > *ctx, tree t, > if (*non_constant_p) > return t; > > + if (integer_zerop (op0)) > +{ > + if (!ctx->quiet) > +error ("dereferencing a null pointer"); > + *non_constant_p = true; > + return t; > +} I'm skeptical of checking this here, since *p is valid for null p; &*p is even a constant expression. And removing this hunk doesn't seem to break any of your tests. OK with that hunk removed. >>> >>> >>> With it removed the constexpr-nullptr-2.C test fails on line 64: >>> >>>constexpr const int *pi0 = >pa1->pa0->i; // { dg-error "null >>> pointer|not a constant" } >>> >>> Here, pa2 and pa1 are non-null but pa0 is null. >> >> >> It doesn't fail for me; that line hits the error in >> cxx_eval_component_reference. I'm only talking about removing the >> cxx_eval_indirect_ref hunk. > > > Sorry, I may have been referring to an older patch. With the latest > patch, the assertion is on line 75. It's also not failing, even > though it should be. The problem is that I had misunderstood how > the vertical bar in DejaGnu directives works. I thought it meant > that both sides had to match a message on that line, when it means > only one side has to. I'll need to fix that (how does one match > two messages on the same line?) > > But removing the hunk as you suggest does break the intent of the > test. With it there, we get a descriptive message for the invalid > code below clearly explaining the problem: > > $ cat xyz.c && /build/gcc-60760/gcc/xgcc -B /build/gcc-60760/gcc -S -Wall > -Wextra -Wpedantic -xc++ xyz.c > struct S { const S *p; int i; }; > > constexpr S s0 = { 0, 0 }; > constexpr S s1 = { , 1 }; > > constexpr int i = s1.p->p->i; > xyz.c:6:28: error: dereferencing a null pointer > constexpr int i = s1.p->p->i; > ^ > > With the hunk removed, all we get is the generic: > > xyz.c:6:28: error: ‘*(const S*)((const S*)s1.S::p)->S::p’ is not a constant > expression > constexpr int i = s1.p->p->i; > ^ > > Re-reading your comment above now: "since *p is valid for null p;" > I agree that &*p is valid when p is null. Unless I missed a case > it is accepted with or without the hunk. Otherwise, *p is not valid, > and it is also rejected with or without it. > > Is there something else you're worried about with the hunk that > makes you want to trade it off for the less informative message? OK, we can keep the hunk, but only when !lval, since that means we access the value. Jason
Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
On 07/20/2016 07:52 AM, Jason Merrill wrote: On Mon, Jul 18, 2016 at 6:15 PM, Martin Seborwrote: On 07/18/2016 11:51 AM, Jason Merrill wrote: On 07/06/2016 06:20 PM, Martin Sebor wrote: @@ -2911,6 +2923,14 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t, if (*non_constant_p) return t; + if (integer_zerop (op0)) +{ + if (!ctx->quiet) +error ("dereferencing a null pointer"); + *non_constant_p = true; + return t; +} I'm skeptical of checking this here, since *p is valid for null p; &*p is even a constant expression. And removing this hunk doesn't seem to break any of your tests. OK with that hunk removed. With it removed the constexpr-nullptr-2.C test fails on line 64: constexpr const int *pi0 = >pa1->pa0->i; // { dg-error "null pointer|not a constant" } Here, pa2 and pa1 are non-null but pa0 is null. It doesn't fail for me; that line hits the error in cxx_eval_component_reference. I'm only talking about removing the cxx_eval_indirect_ref hunk. Sorry, I may have been referring to an older patch. With the latest patch, the assertion is on line 75. It's also not failing, even though it should be. The problem is that I had misunderstood how the vertical bar in DejaGnu directives works. I thought it meant that both sides had to match a message on that line, when it means only one side has to. I'll need to fix that (how does one match two messages on the same line?) But removing the hunk as you suggest does break the intent of the test. With it there, we get a descriptive message for the invalid code below clearly explaining the problem: $ cat xyz.c && /build/gcc-60760/gcc/xgcc -B /build/gcc-60760/gcc -S -Wall -Wextra -Wpedantic -xc++ xyz.c struct S { const S *p; int i; }; constexpr S s0 = { 0, 0 }; constexpr S s1 = { , 1 }; constexpr int i = s1.p->p->i; xyz.c:6:28: error: dereferencing a null pointer constexpr int i = s1.p->p->i; ^ With the hunk removed, all we get is the generic: xyz.c:6:28: error: ‘*(const S*)((const S*)s1.S::p)->S::p’ is not a constant expression constexpr int i = s1.p->p->i; ^ Re-reading your comment above now: "since *p is valid for null p;" I agree that &*p is valid when p is null. Unless I missed a case it is accepted with or without the hunk. Otherwise, *p is not valid, and it is also rejected with or without it. Is there something else you're worried about with the hunk that makes you want to trade it off for the less informative message? Martin
Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
On Mon, Jul 18, 2016 at 6:15 PM, Martin Seborwrote: > On 07/18/2016 11:51 AM, Jason Merrill wrote: >> >> On 07/06/2016 06:20 PM, Martin Sebor wrote: >>> >>> @@ -2911,6 +2923,14 @@ cxx_eval_indirect_ref (const constexpr_ctx >>> *ctx, tree t, >>>if (*non_constant_p) >>> return t; >>> >>> + if (integer_zerop (op0)) >>> +{ >>> + if (!ctx->quiet) >>> +error ("dereferencing a null pointer"); >>> + *non_constant_p = true; >>> + return t; >>> +} >> >> I'm skeptical of checking this here, since *p is valid for null p; &*p >> is even a constant expression. And removing this hunk doesn't seem to >> break any of your tests. >> >> OK with that hunk removed. > > With it removed the constexpr-nullptr-2.C test fails on line 64: > > constexpr const int *pi0 = >pa1->pa0->i; // { dg-error "null > pointer|not a constant" } > > Here, pa2 and pa1 are non-null but pa0 is null. It doesn't fail for me; that line hits the error in cxx_eval_component_reference. I'm only talking about removing the cxx_eval_indirect_ref hunk. Jason
Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
On 07/18/2016 11:51 AM, Jason Merrill wrote: On 07/06/2016 06:20 PM, Martin Sebor wrote: @@ -2911,6 +2923,14 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t, if (*non_constant_p) return t; + if (integer_zerop (op0)) +{ + if (!ctx->quiet) +error ("dereferencing a null pointer"); + *non_constant_p = true; + return t; +} I'm skeptical of checking this here, since *p is valid for null p; &*p is even a constant expression. And removing this hunk doesn't seem to break any of your tests. OK with that hunk removed. With it removed the constexpr-nullptr-2.C test fails on line 64: constexpr const int *pi0 = >pa1->pa0->i; // { dg-error "null pointer|not a constant" } Here, pa2 and pa1 are non-null but pa0 is null. Martin
Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
On 07/06/2016 06:20 PM, Martin Sebor wrote: @@ -2911,6 +2923,14 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t, if (*non_constant_p) return t; + if (integer_zerop (op0)) + { + if (!ctx->quiet) + error ("dereferencing a null pointer"); + *non_constant_p = true; + return t; + } I'm skeptical of checking this here, since *p is valid for null p; &*p is even a constant expression. And removing this hunk doesn't seem to break any of your tests. OK with that hunk removed. Jason
Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
Ping. Jason, do you have any further comments or concerns with the updated patch? https://gcc.gnu.org/ml/gcc-patches/2016-07/msg00280.html Thanks Martin On 07/06/2016 04:20 PM, Martin Sebor wrote: On 06/23/2016 03:36 PM, Jason Merrill wrote: On 06/20/2016 10:17 PM, Martin Sebor wrote: + && tree_int_cst_equal (lhs, null_pointer_node) + && !tree_int_cst_equal (rhs, integer_zero_node)) Not integer_zerop? +"invalid conversion involving a null pointer"); ... +"invalid conversion from %qT to %qT", The conversion isn't invalid, it just isn't a constant expression. (Sorry for the delay following up on this review. I got busy with something else.) I've adjusted the text of the diagnostics, though the first one is also issued for conversions that are invalid even outside constexpr, such as those that cast away constness, or those that cast to incomplete type. Without -fpermissve those are already diagnosed by this point but I'm not sure how much trouble to go to here to avoid diagnosing them again, or at all with -fpermissve. For the null pointer to pointer conversion, does this properly allow conversion to void* or to base*? It didn't handle either but does now. Thank you for calling it out. Surprisingly, a regression run including libstdc++ didn't catch it. I've added tests to exercise it. +if (integer_zerop (op)) ... + else if (!integer_zerop (op)) The second test seems redundant. I have removed it. Martin
Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
On 06/23/2016 03:36 PM, Jason Merrill wrote: On 06/20/2016 10:17 PM, Martin Sebor wrote: + && tree_int_cst_equal (lhs, null_pointer_node) + && !tree_int_cst_equal (rhs, integer_zero_node)) Not integer_zerop? +"invalid conversion involving a null pointer"); ... +"invalid conversion from %qT to %qT", The conversion isn't invalid, it just isn't a constant expression. (Sorry for the delay following up on this review. I got busy with something else.) I've adjusted the text of the diagnostics, though the first one is also issued for conversions that are invalid even outside constexpr, such as those that cast away constness, or those that cast to incomplete type. Without -fpermissve those are already diagnosed by this point but I'm not sure how much trouble to go to here to avoid diagnosing them again, or at all with -fpermissve. For the null pointer to pointer conversion, does this properly allow conversion to void* or to base*? It didn't handle either but does now. Thank you for calling it out. Surprisingly, a regression run including libstdc++ didn't catch it. I've added tests to exercise it. +if (integer_zerop (op)) ... + else if (!integer_zerop (op)) The second test seems redundant. I have removed it. Martin 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/cp/ChangeLog: 2016-07-06 Martin SeborPR 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: 2016-07-06 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. diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index ba40435..83954d8 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -1811,6 +1811,13 @@ 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 + && 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); @@ -2151,6 +2158,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) @@ -2911,6 +2923,14 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t, if (*non_constant_p) return t; + if (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, _base); if (r == NULL_TREE) @@ -3559,10 +3579,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) @@ -3973,7 +4005,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, @@ -3999,15 +4030,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 (!ctx->quiet) - error_at (EXPR_LOC_OR_LOC (t, input_location), -
Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
On 06/20/2016 10:17 PM, Martin Sebor wrote: + && tree_int_cst_equal (lhs, null_pointer_node) + && !tree_int_cst_equal (rhs, integer_zero_node)) Not integer_zerop? + "invalid conversion involving a null pointer"); ... + "invalid conversion from %qT to %qT", The conversion isn't invalid, it just isn't a constant expression. For the null pointer to pointer conversion, does this properly allow conversion to void* or to base*? + if (integer_zerop (op)) ... + else if (!integer_zerop (op)) The second test seems redundant. Jason
Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
+ 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 (t) == INTEGER_CST + && TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE + && !integer_zerop (t)) +{ + if (!ctx->quiet) +error ("arithmetic involving a null pointer in %qE", t); +} These places should all set *non_constant_p, and the second should return t after doing so. OK with that change. Some additional testing exposed a couple of bugs in the patch. First, adding or subtracting a constant zero to or from a null pointer is valid (it was rejected with the previous patch). Second, the code in cxx_eval_constant_expression that tried to detect invalid conversions didn't handle qualifiers correctly in all cases, causing conversions that add constness to be rejected. Finally, while fixing these issues I decided that dereferencing and indirecting through null pointers would be more appropriately handled in the functions that deal with those expressions rather than in cxx_eval_constant_expression. Since the updates aren't completely trivial I post the new version of the patch for another review before committing it. Thanks Martin 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/cp/ChangeLog: 2016-06-20 Martin SeborPR 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: 2016-06-20 Martin Sebor PR c++/60760 PR c++/71091 * g++.dg/cpp0x/constexpr-nullptr-2.C: New test. * g++.dg/cpp1y/constexpr-sfinae.C: Correct. * g++.dg/ubsan/pr63956.C: Correct. Index: gcc/cp/constexpr.c === --- gcc/cp/constexpr.c (revision 237582) +++ gcc/cp/constexpr.c (working copy) @@ -1811,6 +1811,14 @@ cxx_eval_binary_expression (const constexpr_ctx *c || 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) + && !tree_int_cst_equal (rhs, integer_zero_node)) +{ + 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); @@ -2151,6 +2159,11 @@ cxx_eval_component_reference (const constexpr_ctx 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) @@ -2911,6 +2924,14 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, t if (*non_constant_p) return t; + if (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, _base); if (r == NULL_TREE) @@ -3559,10 +3580,20 @@ cxx_eval_constant_expression (const constexpr_ctx 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 ("arithmetic involving a null pointer in %qE", t); +} + return t; } - switch (TREE_CODE (t)) + tree_code tcode = TREE_CODE (t); + switch (tcode) { case RESULT_DECL: if (lval) @@ -3973,7 +4004,6 @@ cxx_eval_constant_expression (const constexpr_ctx 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, @@ -3999,15 +4029,43 @@ cxx_eval_constant_expression (const constexpr_ctx 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) + { + if (integer_zerop (op)) +
Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
On 06/01/2016 10:49 PM, Martin Sebor wrote: On 06/01/2016 01:20 PM, Jason Merrill wrote: On 06/01/2016 02:44 PM, Martin Sebor wrote: 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; Note that this falls under core issue 1530, which has not been resolved. I don't quite see the relevance of this issue. It's concerned with storage in which an object will exist at some point in the future when its lifetime begins or where it existed in the past before its lifetime ended. There is no object or storage at s.s above because s.s is null. True. Unfortunately there isn't any wording about what you can do with the result of indirecting a null pointer or pointer to one-past-the-end of an array. There was some proposed for issue 232, but that hasn't been a high priority. What do you see in the standard currently that makes it undefined? In N4567, expr.ref, the E1->E2 expression which is equivalent to (*E1).E2, the closest match is paragraph 4.2: If E2 is a non-static data member and the type of E1 is "cq1 vq1 X", and the type of E2 is "cq2 vq2 T", the expression designates the named member of the object designated by the first expression. There is no object, so 4.2 doesn't apply, and since there is no other bullet that would apply, the behavior is undefined. OK, I'll buy that. + if (code == POINTER_PLUS_EXPR && !*non_constant_p + && tree_int_cst_equal (lhs, null_pointer_node)) +{ + if (!ctx->quiet) +error ("arithmetic involving a null pointer in %qE", lhs); + return t; +} + 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 (t) == INTEGER_CST + && TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE + && !integer_zerop (t)) +{ + if (!ctx->quiet) +error ("arithmetic involving a null pointer in %qE", t); +} These places should all set *non_constant_p, and the second should return t after doing so. OK with that change. Thanks. I've made the change, but I haven't managed to come up with a test to exercise it. IIUC, the purpose setting non_constant_p while the quiet flag is set is to determine without causing errors that expressions are not valid constant expressions by passing them to __builtin_constant_p, correct? No, __builtin_constant_p allows more things than C++ constant expressions. The purpose of setting *non_constant_p is to cause maybe_constant_value to return its argument unchanged. Jason
Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
On 06/01/2016 01:20 PM, Jason Merrill wrote: On 06/01/2016 02:44 PM, Martin Sebor wrote: 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; Note that this falls under core issue 1530, which has not been resolved. I don't quite see the relevance of this issue. It's concerned with storage in which an object will exist at some point in the future when its lifetime begins or where it existed in the past before its lifetime ended. There is no object or storage at s.s above because s.s is null. True. Unfortunately there isn't any wording about what you can do with the result of indirecting a null pointer or pointer to one-past-the-end of an array. There was some proposed for issue 232, but that hasn't been a high priority. What do you see in the standard currently that makes it undefined? In N4567, expr.ref, the E1->E2 expression which is equivalent to (*E1).E2, the closest match is paragraph 4.2: If E2 is a non-static data member and the type of E1 is "cq1 vq1 X", and the type of E2 is "cq2 vq2 T", the expression designates the named member of the object designated by the first expression. There is no object, so 4.2 doesn't apply, and since there is no other bullet that would apply, the behavior is undefined. It might perhaps be okay to accept the expression above as an extension with the rationale that the offset of the first member is zero and so its address must be a null pointer, but it surely wouldn't be okay in the modified test case below where the second member's offset is non-zero and there may not be a way to represent that offset as a pointer. Why not? It would be a pointer to address 4 or whatever. Because the hardware may simply not have a representation for 4 (or any small integer) in a pointer type. Possibly because it uses that bit pattern for some special mapping and when there is no mapping, using the invalid pointer value traps. Whether or not this is the case is implementation-defined (certainly in C for this reason, and I expect also in C++). For core constant expressions, my impression is that they are deliberately constrained to a fairly narrow guaranteed- to-be-valid subset, which is why things like reinterpret_cast are not allowed. The expression we're discussing basically boils down to a conversion of the offset 4 to a pointer, which is equivalent to the forbidden reinterpret_cast. I agree that the standard text isn't ironclad on this, but I can't think of a useful purpose that allowing constexpr expressions to form invalid addresses would serve even in the (common) case where the hardware does allow it. From a practical point of view, if GCC were to continue to allow it, it would need to track those pointers and diagnose them on their first use, because that is undefined in any case. + if (code == POINTER_PLUS_EXPR && !*non_constant_p + && tree_int_cst_equal (lhs, null_pointer_node)) +{ + if (!ctx->quiet) +error ("arithmetic involving a null pointer in %qE", lhs); + return t; +} + 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 (t) == INTEGER_CST + && TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE + && !integer_zerop (t)) +{ + if (!ctx->quiet) +error ("arithmetic involving a null pointer in %qE", t); +} These places should all set *non_constant_p, and the second should return t after doing so. OK with that change. Thanks. I've made the change, but I haven't managed to come up with a test to exercise it. IIUC, the purpose setting non_constant_p while the quiet flag is set is to determine without causing errors that expressions are not valid constant expressions by passing them to __builtin_constant_p, correct? If so, then there must be something wrong somewhere because the test case below fails the static assertion (the first error is expected with the patch): $ cat null.c && /home/msebor/build/gcc-60760/gcc/xgcc -B /home/msebor/build/gcc-60760/gcc -S -Wall -Wextra -Wpedantic -xc++ null.c constexpr int *p = 0; constexpr int *q = p + 1; static_assert (!__builtin_constant_p (p + 1), "<<<"); null.c:2:24: error: arithmetic involving a null pointer in ‘0u’ constexpr int *q = p + 1; ^ null.c:4:1: error: static assertion failed: <<< static_assert (!__builtin_constant_p (p + 1), "<<<"); ^ (I know of at least one bug here, c++/70552, but this one is new.) Martin
Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
On 06/01/2016 02:44 PM, Martin Sebor wrote: 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; Note that this falls under core issue 1530, which has not been resolved. I don't quite see the relevance of this issue. It's concerned with storage in which an object will exist at some point in the future when its lifetime begins or where it existed in the past before its lifetime ended. There is no object or storage at s.s above because s.s is null. True. Unfortunately there isn't any wording about what you can do with the result of indirecting a null pointer or pointer to one-past-the-end of an array. There was some proposed for issue 232, but that hasn't been a high priority. What do you see in the standard currently that makes it undefined? It might perhaps be okay to accept the expression above as an extension with the rationale that the offset of the first member is zero and so its address must be a null pointer, but it surely wouldn't be okay in the modified test case below where the second member's offset is non-zero and there may not be a way to represent that offset as a pointer. Why not? It would be a pointer to address 4 or whatever. Thanks for the hint. I've made that change and also adjusted the rest of the patch to avoid passing the nullptr_p around. It was surprisingly simpler to do than I remembered from my first attempt back in March. + if (code == POINTER_PLUS_EXPR && !*non_constant_p + && tree_int_cst_equal (lhs, null_pointer_node)) +{ + if (!ctx->quiet) +error ("arithmetic involving a null pointer in %qE", lhs); + return t; +} + 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 (t) == INTEGER_CST + && TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE + && !integer_zerop (t)) +{ + if (!ctx->quiet) +error ("arithmetic involving a null pointer in %qE", t); +} These places should all set *non_constant_p, and the second should return t after doing so. OK with that change. Jason
Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
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; Note that this falls under core issue 1530, which has not been resolved. I don't quite see the relevance of this issue. It's concerned with storage in which an object will exist at some point in the future when its lifetime begins or where it existed in the past before its lifetime ended. There is no object or storage at s.s above because s.s is null. It might perhaps be okay to accept the expression above as an extension with the rationale that the offset of the first member is zero and so its address must be a null pointer, but it surely wouldn't be okay in the modified test case below where the second member's offset is non-zero and there may not be a way to represent that offset as a pointer. This seems unquestionably undefined to me. Did I miss something? struct S { const S *s; int i; }; constexpr S s = { 0 }; constexpr const void *p = >i; 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. Thanks for the hint. I've made that change and also adjusted the rest of the patch to avoid passing the nullptr_p around. It was surprisingly simpler to do than I remembered from my first attempt back in March. Martin 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 SeborPR 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 PR c++/60760 PR c++/71091 * constexpr.c (cxx_eval_binary_expression): Reject invalid arithmetic involving null pointers. (cxx_eval_constant_expression): Same. (cxx_eval_component_reference): Reject null pointer dereferences. diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 482f8af..d1fe276 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -1757,6 +1757,13 @@ 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 a null pointer in %qE", lhs); + return t; +} if (r == NULL_TREE) r = fold_binary_loc (loc, code, type, lhs, rhs); @@ -2097,6 +2104,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) @@ -3505,10 +3517,20 @@ 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 ("arithmetic involving a null pointer in %qE", t); +} + return t; } - switch (TREE_CODE (t)) + tree_code tcode = TREE_CODE (t); + switch (tcode) { case RESULT_DECL: if (lval) @@ -3919,7 +3941,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, @@ -3945,15 +3966,26 @@ 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
Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
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; 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
Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
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; Martin
Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
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