On Thu, Sep 03, 2026 at 08:02:20PM -0600, Gustavo A. R. Silva wrote:
> 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.

Looking at the PR:

struct flex {
  size_t count;
  char fam[] __attribute__((counted_by(count)));
};                    /* sizeof(struct flex) == 8  */

struct outer {
  int hdr;
  struct flex inner;
};                    /* sizeof(struct outer) == 16 */

int main (void)
{
  struct outer *p = __builtin_malloc (sizeof (*p) + 48); /* 64-byte object */

  /* This is a bug. */
  expect(__builtin_object_size (&p->inner, 1), sizeof(p->inner));
  expect(__builtin_dynamic_object_size (&p->inner, 1), sizeof(p->inner));
...
}

Before:

WAT: __builtin_object_size (&p->inner, 1) == 56 (expected 8)
WAT: __builtin_dynamic_object_size (&p->inner, 1) == 56 (expected 8)

After:

ok:  __builtin_object_size (&p->inner, 1) == 8
ok:  __builtin_dynamic_object_size (&p->inner, 1) == 8


This makes sense to me, the dynamic size of "fam" is ambiguous between
being 0 or 48 (from the "alloc_size" attribute), so type 1 chooses the
smaller.

I would, however, expect the use of counted_by to change it. For
example, if it were this:

struct flex {
  size_t count;
  char fam[] __attribute__((counted_by(count)));
};
...
  struct outer *p = __builtin_malloc (sizeof (*p) + 48); /* 64-byte object */
  p->inner.count = 48;

I would expect the results to be:

ok:  __builtin_object_size (&p->inner, 1) == 8          /* compile-time size */
ok:  __builtin_dynamic_object_size (&p->inner, 1) == 56 /* run-time size */

As the known size of p->inner is everything contained by "inner" and
that now includes the 48 bytes of "fam".

-Kees

-- 
Kees Cook

Reply via email to