All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2] tree-object-size: Fix type-1 size for FAM subobjects under -fstrict-flex-arrays=3 [PR126975]
@ 2026-09-04  2:02 Gustavo A. R. Silva
  0 siblings, 0 replies; only message in thread
From: Gustavo A. R. Silva @ 2026-09-04  2:02 UTC (permalink / raw)
  To: Jakub Jelinek, Richard Biener, Siddhesh Poyarekar, Kees Cook
  Cc: gcc-patches, Martin Uecker, josmyers, Bill Wendling,
	linux-hardening, Gustavo A. R. Silva

For a pointer to a subobject whose record/union type ends in a flexible-array
member (directly, or through a trailing nested struct), addr_object_size()
walked up to the enclosing object (v = TREE_OPERAND (v, 0)) instead of
measuring the referenced subobject.  __builtin_object_size() and
__builtin_dynamic_object_size() type 1 therefore returned the whole-object
size, collapsing type 1 onto type 0 and losing the distinction between
&p->inner and p.  FORTIFY_SOURCE relies on the type-1 distinction, so this
weakens its bounds checks.

Fix this by computing the size directly from the referenced record/union
instead of walking up, under -fstrict-flex-arrays=3 specifically to avoid
conflicting with -fstrict-flex-arrays semantics at lower levels, thereby
restoring the type-0/type-1 distinction that Clang already implements.

Bootstrapped and regtested on x86_64-linux-gnu.

Changes in v2:
 - Change behavior under -fstrict-flex-arrays=3 only.
 - Update subject line and changelog text.
 - Document the new type-1 behavior in extend.texi.

v1:
 - Link: https://lore.kernel.org/linux-hardening/aojDH2Db6XIkVqcf@kspp/

	PR tree-optimization/126975

gcc/ChangeLog:

	* tree-object-size.cc (addr_object_size): For a reference to a
	record or union type that recursively includes a flexible-array
	member, compute the object size from the referenced subobject
	instead of walking up to the enclosing object when
	-fstrict-flex-arrays=3 is in effect.
	* doc/extend.texi (__builtin_object_size): Document the type-1
	behavior for pointers to subobjects that end in a flexible array
	member, and its interaction with -fstrict-flex-arrays=3.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-object-size-pr126975.c: New test.

Signed-off-by: Gustavo A. R. Silva <gustavoars@kernel.org>
---
 gcc/doc/extend.texi                           | 32 +++++++++++++
 .../gcc.dg/builtin-object-size-pr126975.c     | 45 +++++++++++++++++++
 gcc/tree-object-size.cc                       |  7 ++-
 3 files changed, 82 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/builtin-object-size-pr126975.c

diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 397b05ab87d..30fdc702f13 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -17894,6 +17894,38 @@ assert (__builtin_object_size (q, 0)
 /* The subobject q points to is var.b.  */
 assert (__builtin_object_size (q, 1) == sizeof (var.b));
 @end smallexample
+
+When, for @var{type} 1, the closest surrounding subobject is of a
+@code{struct} or @code{union} type that ends in a flexible array
+(@pxref{Zero Length,,Arrays of Length Zero}), either directly or through a
+trailing nested struct, that subobject can be extended past its declared
+size.  Its size is therefore constrained only by the enclosing complete
+object, as it is for @var{type} 0.
+
+Under @option{-fstrict-flex-arrays=3}, where only a true C99
+flexible array member is treated as a flexible array, the declared size of
+the referenced subobject is used instead, which restores the distinction
+between @var{type} 0 and @var{type} 1 for such pointers.
+
+@smallexample
+/* Compiled with -fstrict-flex-arrays=3.  */
+struct A @{ int n; char data[]; @};
+struct B @{ int m; struct A a; @};
+struct B *b = malloc (sizeof (struct B) + 48);
+struct B *volatile vp = malloc (sizeof (struct B) + 48);
+struct B *op = vp;
+
+/* &b->a ends in a flexible array member, but -fstrict-flex-arrays=3 makes
+   type 1 its declared size, not the whole-object (type 0) size.  */
+assert (__builtin_object_size (&b->a, 1) == sizeof (b->a));
+/* Likewise when op is opaque, where type 0 would be (size_t) -1.  */
+assert (__builtin_object_size (&op->a, 1) == sizeof (op->a));
+
+/* For a pointer to the flexible array member itself, type 0 and type 1
+   both return (size_t) -1 here.  */
+assert (__builtin_object_size (op->a.data, 0) == (size_t) -1);
+assert (__builtin_object_size (op->a.data, 1) == (size_t) -1);
+@end smallexample
 @enddefbuiltin
 
 @defbuiltin{{size_t} __builtin_dynamic_object_size (const void * @var{ptr}, int @var{type})}
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-pr126975.c b/gcc/testsuite/gcc.dg/builtin-object-size-pr126975.c
new file mode 100644
index 00000000000..c8c60c4f76e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr126975.c
@@ -0,0 +1,45 @@
+/* PR 126975:
+   Under -fstrict-flex-arrays=3 only a true C99 flexible-array member is
+   treated as flexible. For &subobject whose type ends in such a member,
+   __builtin_object_size/__builtin_dynamic_object_size type 1 must measure
+   the subobject, not the tail of the allocation (type 0). */
+/* { dg-do run } */
+/* { dg-options "-O2 -fstrict-flex-arrays=3" } */
+
+#include "builtin-object-size-common.h"
+
+struct flex {
+  size_t count;
+  char fam[];
+};
+
+struct outer {
+  int hdr;
+  struct flex inner;
+};
+
+int main (void)
+{
+  struct outer *p = __builtin_malloc (sizeof(*p) + 48);
+  struct outer *volatile vp = __builtin_malloc (sizeof(*vp) + 48);
+  struct outer *op = vp;
+
+  /* True C99 flexible-array member: type 1 measures the subobject. */
+  EXPECT(__builtin_object_size(&p->inner, 1), sizeof(p->inner));
+  EXPECT(__builtin_dynamic_object_size(&p->inner, 1), sizeof(p->inner));
+
+  /* Type 0 still reports the whole tail of the allocation. */
+  EXPECT(__builtin_object_size(&p->inner, 0), sizeof(p->inner) + 48);
+  EXPECT(__builtin_dynamic_object_size(&p->inner, 0), sizeof(p->inner) + 48);
+
+  /* When op is opaque, type 0 is unknown (-1), but type 1 still measures
+     the subobject. */
+  EXPECT(__builtin_object_size(&op->inner, 0), -1);
+  EXPECT(__builtin_object_size(&op->inner, 1), sizeof(op->inner));
+  EXPECT(__builtin_dynamic_object_size(&op->inner, 1), sizeof(op->inner));
+
+  __builtin_free (p);
+  __builtin_free (op);
+
+  DONE ();
+}
diff --git a/gcc/tree-object-size.cc b/gcc/tree-object-size.cc
index 54c320d36d0..90d2ac32614 100644
--- a/gcc/tree-object-size.cc
+++ b/gcc/tree-object-size.cc
@@ -734,10 +734,13 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 		      }
 		    /* if the ref is to a record or union type, but the type
 		       does not include a flexible array recursively, compute
-		       the object size directly.  */
+		       the object size directly.  With -fstrict-flex-arrays=3 do
+		       the same even when it does, so type 1 measures the subobject
+		       instead of collapsing onto the whole-object type-0 size.  */
 		    if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (v)))
 		      {
-			if (!TYPE_INCLUDES_FLEXARRAY (TREE_TYPE (v)))
+			if (!TYPE_INCLUDES_FLEXARRAY (TREE_TYPE (v))
+			    || flag_strict_flex_arrays == 3)
 			  {
 			    v = NULL_TREE;
 			    break;
-- 
2.47.3


^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-09-03 11:03 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-04  2:02 [PATCH v2] tree-object-size: Fix type-1 size for FAM subobjects under -fstrict-flex-arrays=3 [PR126975] Gustavo A. R. Silva

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.