[Bug tree-optimization/126975] New: __bos/__bdos(&p->inner, 1) collapses into type 0 when subobject inner ends in a FAM

gustavo at embeddedor dot com via Gcc-bugs <[email protected]>
Newsgroups gmane.comp.gcc.bugs
Message-ID <[email protected]/bugzilla/>
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)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.