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
>
>

Reply via email to