https://gcc.gnu.org/g:ce93e13e31a4886ce6f16a6b0963838885c0297d

commit r17-379-gce93e13e31a4886ce6f16a6b0963838885c0297d
Author: Jakub Jelinek <[email protected]>
Date:   Thu May 7 15:50:14 2026 +0200

    c++: Constant evaluation of __builtin_{,dynamic_}object_size [PR124347]
    
    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.
    
    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.
    
    Reviewed-by: Jason Merrill <[email protected]>

Diff:
---
 gcc/cp/constexpr.cc                             | 12 ++++
 gcc/testsuite/g++.dg/ext/builtin-object-size4.C | 62 ++++++++++++++++++
 gcc/testsuite/g++.dg/ext/builtin-object-size5.C | 83 +++++++++++++++++++++++++
 gcc/testsuite/g++.dg/ext/builtin-object-size6.C | 40 ++++++++++++
 4 files changed, 197 insertions(+)

diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index 280843660c90..9916b63d4287 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -2535,6 +2535,7 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, 
tree t, tree fun,
 
   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 constexpr_ctx 
*ctx, tree t, tree fun,
          }
        *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 constexpr_ctx 
*ctx, tree t, tree fun,
 
       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;
diff --git a/gcc/testsuite/g++.dg/ext/builtin-object-size4.C 
b/gcc/testsuite/g++.dg/ext/builtin-object-size4.C
new file mode 100644
index 000000000000..ce8d85ee7590
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/builtin-object-size4.C
@@ -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, "");
diff --git a/gcc/testsuite/g++.dg/ext/builtin-object-size5.C 
b/gcc/testsuite/g++.dg/ext/builtin-object-size5.C
new file mode 100644
index 000000000000..8d275fd3eddd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/builtin-object-size5.C
@@ -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 ());
diff --git a/gcc/testsuite/g++.dg/ext/builtin-object-size6.C 
b/gcc/testsuite/g++.dg/ext/builtin-object-size6.C
new file mode 100644
index 000000000000..90b695d01f43
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/builtin-object-size6.C
@@ -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);

Reply via email to