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)

Reply via email to