Linux Hardening
 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox