https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126975
Bug ID: 126975
Summary: __bos/__bdos(&p->inner, 1) collapses into type 0 when
subobject inner ends in a FAM
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: tree-optimization
Assignee: unassigned at gcc dot gnu.org
Reporter: gustavo at embeddedor dot com
Target Milestone: ---
For &p->inner below, where inner's type ends in a flexible-array member
(directly, or through a trailing nested struct),
__builtin_object_size(&p->inner, 1) and
__builtin_dynamic_object_size(&p->inner, 1) return the size of the _enclosing_
object instead of the referenced subobject.
#include <stdlib.h>
#define expect(p, _v) do { \
size_t v = _v; \
if (p == v) \
__builtin_printf ("ok: %s == %zd\n", #p, p); \
else {\
__builtin_printf ("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
} \
} while (0);
struct flex {
size_t count;
char fam[];
}; /* 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));
/* This is fine. */
expect(__builtin_object_size (&p->inner, 0), sizeof(p->inner) + 48);
expect(__builtin_dynamic_object_size (&p->inner, 0), sizeof(p->inner) + 48);
free (p);
return 0;
}
output:
WAT: __builtin_object_size (&p->inner, 1) == 56 (expected 8)
WAT: __builtin_dynamic_object_size (&p->inner, 1) == 56 (expected 8)
ok: __builtin_object_size (&p->inner, 0) == 56
ok: __builtin_dynamic_object_size (&p->inner, 0) == 56
(With -O2, x86_64. Reproducer: https://godbolt.org/z/1bP77oqr3)
Type 1 collapses onto type 0, so &p->inner can no longer be distinguished from
p, which weakens FORTIFY_SOURCE (as seen in the Linux kernel). Clang returns
the correct subobject size.
&p->inner names a struct flex (the last member of struct outer), so the
subobject's static size is sizeof(struct flex) == 8.
call current expected
__builtin_object_size (&p->inner, 1) 56 8 WRONG
__builtin_dynamic_object_size(&p->inner, 1) 56 8 WRONG
__builtin_object_size (&p->inner, 0) 56 56 OK
__builtin_dynamic_object_size(&p->inner, 0) 56 56 OK
56 is the size from &p->inner to the end of the 64-byte allocation (type 0).
Type 1 has collapsed onto type 0.
In addr_object_size() (gcc/tree-object-size.cc), when the referenced
record/union type ends in a flexible-array member, the code walks up to the
enclosing object (v = TREE_OPERAND (v, 0)) instead of measuring the referenced
subobject, so type 1 returns the type-0 size.
(I'll submit a patch for this, shortly)