https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127234

            Bug ID: 127234
           Summary: __bdos(&p->inner, 0) misses counted_by 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
annotated with counted_by, __builtin_dynamic_object_size(&p->inner, 0) is
derived from the allocation instead of from counted_by, hence it returns the
number of bytes from &p->inner to the end of the surrounding allocation when
the allocation is visible, and (size_t)-1 when the allocation is hidden.

This is a bug. The size should be determined based on the information provided
by counted_by, that is, __builtin_dynamic_object_size(&p->inner, 0) should
return sizeof(p->inner) + p->inner.count, in this particular case, regardless
of whether the allocation is visible or not. So, counted_by must be
authoritative.

#include <stdlib.h>

#define noinline __attribute__((noinline))
#define __counted_by(member) __attribute__((counted_by(member)))

#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 {   /* sizeof 8  */
  size_t count;
  char fam[] __counted_by(count);
};

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

struct outer noinline *noinline_alloc(size_t n)
{
    struct outer *q = __builtin_malloc(sizeof(*q) + n);

    q->inner.count = n + 1; /* intentionally make count > alloc size */

    __builtin_printf("\nvisible allocation (count > alloc):\n");
    /* counted_by must take precedence over the allocation size. */
    expect(__builtin_dynamic_object_size(&q->inner, 0), sizeof(q->inner) +
q->inner.count);

    return q;
}

int main(void)
{
  struct outer *p = __builtin_malloc(sizeof(*p) + 100);
  struct outer *q = noinline_alloc(10); /* hidden base */

  p->inner.count = 10;  /* intentionally make count < alloc size */

  __builtin_printf("\nvisible allocation (count < alloc):\n");
  /* counted_by (count == 10) makes the object 18 bytes, not 108. */
  expect(__builtin_dynamic_object_size(&p->inner, 0), sizeof(p->inner) +
p->inner.count);

  __builtin_printf("\nhidden allocation (count > alloc):\n");
  /* counted_by (count == 11) makes the object 19 bytes, not -1. */
  expect(__builtin_dynamic_object_size(&q->inner, 0), sizeof(q->inner) +
q->inner.count);

  free(p);
  free(q);

  return 0;
}

Output (with GCC trunk, -O2 -Wall):

visible allocation (count > alloc):
WAT: __builtin_dynamic_object_size(&q->inner, 0) == 18 (expected 19)

visible allocation (count < alloc):
WAT: __builtin_dynamic_object_size(&p->inner, 0) == 108 (expected 18)

hidden allocation (count > alloc):
WAT: __builtin_dynamic_object_size(&q->inner, 0) == -1 (expected 19)

(Reproducer: https://godbolt.org/z/a58qf3s3h)

Call                                   GCC        Expected      
__bdos(&q->inner, 0)                   18            19
(visible alloc and count > alloc)

__bdos(&p->inner, 0)                   108           18
(visible alloc and count < alloc)

__bdos(&q->inner, 0)                   -1            19
(hidden alloc and count > alloc)

I reported a similar bug to Clang, and they fixed it up already. So with Clang
we have the following expected output (with Clang trunk, -O2 -Wall):

visible allocation (count > alloc):
ok:  __builtin_dynamic_object_size(&q->inner, 0) == 19

visible allocation (count < alloc):
ok:  __builtin_dynamic_object_size(&p->inner, 0) == 18

hidden allocation (count > alloc):
ok:  __builtin_dynamic_object_size(&q->inner, 0) == 19

(Reproducer: https://godbolt.org/z/a58qf3s3h)

LLVM issue: https://github.com/llvm/llvm-project/issues/205652
Fixed by: https://github.com/llvm/llvm-project/pull/205903


(I'll submit a patch for this, shortly.)

Reply via email to