On Thu, May 7, 2026 at 11:00 AM Jakub Jelinek <[email protected]> wrote:
> Hi! > > The following patch tries to support constexpr folding of > __builtin_object_size and __builtin_dynamic_object_size. > There is already folding of this builtin in builtins.cc, but it doesn't > look > through pointer conversions in the argument, only handles the case where > the argument is ADDR_EXPR (plus of course when inside of the objsz passes > it > handles more through SSA_NAME tracking). > > I've tried to handle this in builtins.cc first, see > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124347#c1 but unfortunately > that resulted in some serious security regressions where some of __bos > or __bdos builtins were folded prematurely, in particular e.g. for cases > where we have __builtin_object_size (something, 1) and the whole object > size is unknown but object size of the element is known. With the patch > it would just return the remaining subobject size, when it actually should > return MIN_EXPR <object_size, remaining subobject size>. For mode 1 > it is conservatively correct, but larger than necessary with security > implications of not caching out of bounds accesses. > > So, this patch instead handles this in the FE, only for manifestly constant > evaluation, by stripping the pointer casts on the first argument in that > case. > > Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? > I have checked the cosntexpr formatting patch for which I wanted this change, and all test passes without the need for workaround. Thank you. > > 2026-05-07 Jakub Jelinek <[email protected]> > > PR c++/124347 > * constexpr.cc (cxx_eval_builtin_function_call): For > mce_true strip nops in first argument of BUILT_IN*_OBJECT_SIZE > if the inner expression is ADDR_EXPR. > > * g++.dg/ext/builtin-object-size4.C: New test. > * g++.dg/ext/builtin-object-size5.C: New test. > * g++.dg/ext/builtin-object-size6.C: New test. > > --- gcc/cp/constexpr.cc.jj 2026-05-07 08:29:24.904858087 +0200 > +++ gcc/cp/constexpr.cc 2026-05-07 10:17:26.469434446 +0200 > @@ -2535,6 +2535,7 @@ cxx_eval_builtin_function_call (const co > > int strops = 0; > int strret = 0; > + bool bos = false; > if (fndecl_built_in_p (fun, BUILT_IN_NORMAL)) > switch (DECL_FUNCTION_CODE (fun)) > { > @@ -2576,6 +2577,10 @@ cxx_eval_builtin_function_call (const co > } > *non_constant_p = true; > return t; > + case BUILT_IN_OBJECT_SIZE: > + case BUILT_IN_DYNAMIC_OBJECT_SIZE: > + bos = ctx->manifestly_const_eval == mce_true; > + break; > default: > break; > } > @@ -2634,6 +2639,13 @@ cxx_eval_builtin_function_call (const co > > args[i] = arg; > } > + if (bos) > + { > + tree arg = args[0]; > + STRIP_NOPS (arg); > + if (TREE_CODE (arg) == ADDR_EXPR) > + args[0] = arg; > + } > > bool save_ffbcp = force_folding_builtin_constant_p; > force_folding_builtin_constant_p |= ctx->manifestly_const_eval == > mce_true; > --- gcc/testsuite/g++.dg/ext/builtin-object-size4.C.jj 2026-05-07 > 10:17:26.503736686 +0200 > +++ gcc/testsuite/g++.dg/ext/builtin-object-size4.C 2026-05-07 > 10:29:28.256603065 +0200 > @@ -0,0 +1,62 @@ > +// PR c++/124347 > +// { dg-do compile { target c++11 } } > + > +using size_t = decltype (sizeof 0); > + > +constexpr size_t > +foo (void *p) > +{ > + return __builtin_object_size (p, 0); > +} > + > +constexpr size_t > +bar (void *p) > +{ > + return __builtin_object_size (p, 1); > +} > + > +constexpr size_t > +baz (void *p) > +{ > + return __builtin_object_size (p, 2); > +} > + > +constexpr size_t > +qux (void *p) > +{ > + return __builtin_object_size (p, 3); > +} > + > +constexpr size_t > +fred (void *p) > +{ > + return __builtin_dynamic_object_size (p, 0); > +} > + > +constexpr size_t > +corge (void *p) > +{ > + return __builtin_dynamic_object_size (p, 1); > +} > + > +constexpr size_t > +garply (void *p) > +{ > + return __builtin_dynamic_object_size (p, 2); > +} > + > +constexpr size_t > +xyzzy (void *p) > +{ > + return __builtin_dynamic_object_size (p, 3); > +} > + > +struct S { char a; int b; char c[32]; } a[10]; > +static_assert (foo (&a[2].c[5]) == &a[9].a - &a[2].c[5] + sizeof (S), ""); > +static_assert (bar (&a[2].c[5]) == 32 - 5, ""); > +static_assert (baz (&a[2].c[5]) == &a[9].a - &a[2].c[5] + sizeof (S), ""); > +static_assert (qux (&a[2].c[5]) == 32 - 5, ""); > +static_assert (fred (&a[2].c[5]) == &a[9].a - &a[2].c[5] + sizeof (S), > ""); > +static_assert (corge (&a[2].c[5]) == 32 - 5, ""); > +static_assert (garply (&a[2].c[5]) == &a[9].a - &a[2].c[5] + sizeof (S), > ""); > +static_assert (xyzzy (&a[2].c[5]) == 32 - 5, ""); > --- gcc/testsuite/g++.dg/ext/builtin-object-size5.C.jj 2026-05-07 > 10:22:36.781350456 +0200 > +++ gcc/testsuite/g++.dg/ext/builtin-object-size5.C 2026-05-07 > 10:30:59.710101728 +0200 > @@ -0,0 +1,83 @@ > +// PR c++/124347 > +// { dg-do compile { target c++20 } } > + > +using size_t = decltype (sizeof 0); > + > +constexpr size_t > +foo (void *p) > +{ > + return __builtin_object_size (p, 0); > +} > + > +constexpr size_t > +bar (void *p) > +{ > + return __builtin_object_size (p, 1); > +} > + > +constexpr size_t > +baz (void *p) > +{ > + return __builtin_object_size (p, 2); > +} > + > +constexpr size_t > +qux (void *p) > +{ > + return __builtin_object_size (p, 3); > +} > + > +constexpr size_t > +fred (void *p) > +{ > + return __builtin_dynamic_object_size (p, 0); > +} > + > +constexpr size_t > +corge (void *p) > +{ > + return __builtin_dynamic_object_size (p, 1); > +} > + > +constexpr size_t > +garply (void *p) > +{ > + return __builtin_dynamic_object_size (p, 2); > +} > + > +constexpr size_t > +xyzzy (void *p) > +{ > + return __builtin_dynamic_object_size (p, 3); > +} > + > +struct S { char a; int b; char c[32]; char d; }; > + > +constexpr bool > +plugh () > +{ > + S *a = new S {}; > + if (foo (a) != sizeof (S) > + || bar (a) != sizeof (S) > + || baz (a) != sizeof (S) > + || qux (a) != sizeof (S) > + || fred (a) != sizeof (S) > + || corge (a) != sizeof (S) > + || garply (a) != sizeof (S) > + || xyzzy (a) != sizeof (S)) > + return false; > + char *b = &a->c[2]; > + if (foo (b) != sizeof (S) - (&a->c[2]- &a->a) > + || bar (b) != 32 - 2 > + || baz (b) != sizeof (S) - (&a->c[2]- &a->a) > + || qux (b) != 32 - 2 > + || fred (b) != sizeof (S) - (&a->c[2]- &a->a) > + || corge (b) != 32 - 2 > + || garply (b) != sizeof (S) - (&a->c[2]- &a->a) > + || xyzzy (b) != 32 - 2) > + return false; > + delete a; > + return true; > +} > + > +static_assert (plugh ()); > --- gcc/testsuite/g++.dg/ext/builtin-object-size6.C.jj 2026-05-07 > 10:41:36.068655061 +0200 > +++ gcc/testsuite/g++.dg/ext/builtin-object-size6.C 2026-05-07 > 10:42:00.936246823 +0200 > @@ -0,0 +1,40 @@ > +// PR c++/124347 > +// { dg-do compile { target c++20 } } > + > +constexpr int > +foo (const char *p) > +{ > + return __builtin_dynamic_object_size (p, 0); > +} > + > +constexpr int > +bar (int x) > +{ > + char *a = new char[x]; > + int b = foo (a); > + delete [] a; > + return b; > +} > + > +constexpr int > +baz (const char *p) > +{ > + return __builtin_object_size (p, 1); > +} > + > +constexpr int > +qux (int x) > +{ > + char *a = new char[x]; > + int b = baz (a); > + delete [] a; > + return b; > +} > + > +static_assert (bar (42) == 42); > +static_assert (qux (142) == 142); > +constexpr char a[] = "foobar"; > +static_assert (foo (a) == 7); > +static_assert (baz (a) == 7); > +static_assert (foo ("foo") == 4); > +static_assert (baz ("baz") == 4); > > Jakub > >
