> On Aug 3, 2023, at 1:51 PM, Kees Cook <k...@kernel.org> wrote: > > On August 3, 2023 10:34:24 AM PDT, Qing Zhao <qing.z...@oracle.com> wrote: >> One thing I need to point out first is, currently, even for regular fixed >> size array in the structure, >> We have this same issue, for example: >> >> #define LENGTH 10 >> >> struct fix { >> size_t foo; >> int array[LENGTH]; >> }; >> >> … >> int main () >> { >> struct fix *p; >> p = alloc_buf_more (); >> >> expect(__builtin_object_size(p->array, 1), LENGTH * sizeof(int)); >> expect(__builtin_object_size(p->array, 0), -1); >> } >> >> Currently, for __builtin_object_size(p->array, 0), GCC return UNKNOWN for >> it. >> This is not a special issue for flexible array member. > > Is this true with -fstrict-flex-arrays=3 ?
Yes. Please see the following testing case: #include <stdint.h> #include <malloc.h> #define LENGTH 10 #define SIZE_BUMP 5 #define noinline __attribute__((__noinline__)) struct fix { size_t foo; int array[LENGTH]; }; #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); /* in the following function, malloc allocated more space than size of the struct fix. Then what's the correct behavior we expect the __builtin_object_size should have for the following? */ static struct fix * noinline alloc_buf_more () { struct fix *p; p = malloc(sizeof (struct fix) + SIZE_BUMP * sizeof (int)); /*when checking the observed access p->array, we have info on both observered allocation and observed access, A. from observed allocation (alloc_size): (LENGTH + SIZE_BUMP) * sizeof (int) B. from observed access (TYPE): LENGTH * sizeof (int) */ /* for MAXIMUM size in the whole object: currently, GCC always used the A. */ expect(__builtin_object_size(p->array, 0), (LENGTH + SIZE_BUMP) * sizeof(int)); /* for MAXIMUM size in the sub-object: currently, GCC chose the smaller one among these two: B. */ expect(__builtin_object_size(p->array, 1), LENGTH * sizeof(int)); return p; } /* in the following function, malloc allocated less space than size of the struct fix. Then what's the correct behavior we expect the __builtin_object_size should have for the following? */ static struct fix * noinline alloc_buf_less () { struct fix *p; p = malloc(sizeof (struct fix) - SIZE_BUMP * sizeof (int)); /*when checking the observed access p->array, we have info on both observered allocation and observed access, A. from observed allocation (alloc_size): (LENGTH - SIZE_BUMP) * sizeof (int) B. from observed access (TYPE): LENGTH * sizeof (int) */ /* for MAXIMUM size in the whole object: currently, GCC always used the A. */ expect(__builtin_object_size(p->array, 0), (LENGTH - SIZE_BUMP) * sizeof(int)); /* for MAXIMUM size in the sub-object: currently, GCC chose the smaller one among these two: B. */ expect(__builtin_object_size(p->array, 1), (LENGTH - SIZE_BUMP) * sizeof(int)); return p; } int main () { struct fix *p, *q; p = alloc_buf_more (); q = alloc_buf_less (); expect(__builtin_object_size(p->array, 1), LENGTH * sizeof(int)); expect(__builtin_object_size(p->array, 0), -1); /*when checking the pointer p, we have no observed allocation nor observed access. therefore, we cannot determine the size info here. */ expect(__builtin_object_size(p, 1), -1); expect(__builtin_object_size(p, 0), -1); /*when checking the observed access p->array, we only have info on the observed access, i.e, the TYPE_SIZE info from the access. We don't have info on the whole object. */ expect(__builtin_object_size(q->array, 1), LENGTH * sizeof(int)); expect(__builtin_object_size(q->array, 0), -1); /*when checking the pointer p, we have no observed allocation nor observed access. therefore, we cannot determine the size info here. */ expect(__builtin_object_size(q, 1), -1); expect(__builtin_object_size(q, 0), -1); return 0; } [opc@qinzhao-ol8u3-x86 108896]$ sh t /home/opc/Install/latest/bin/gcc -O -fstrict-flex-arrays=3 t28.c ok: __builtin_object_size(p->array, 0) == 60 ok: __builtin_object_size(p->array, 1) == 40 ok: __builtin_object_size(p->array, 0) == 20 ok: __builtin_object_size(p->array, 1) == 20 ok: __builtin_object_size(p->array, 1) == 40 ok: __builtin_object_size(p->array, 0) == -1 ok: __builtin_object_size(p, 1) == -1 ok: __builtin_object_size(p, 0) == -1 ok: __builtin_object_size(q->array, 1) == 40 ok: __builtin_object_size(q->array, 0) == -1 ok: __builtin_object_size(q, 1) == -1 ok: __builtin_object_size(q, 0) == -1 [opc@qinzhao-ol8u3-x86 108896]$ > > -Kees > >> >> Qing >> >> >> On Aug 3, 2023, at 1:19 PM, Siddhesh Poyarekar <siddh...@gotplt.org> wrote: >>> >>> On 2023-08-03 12:43, Qing Zhao wrote: >>>>> Surely we could emit that for __bdos(q->array, 0) though, couldn't we? >>>> For __bdos(q->array, 0), we only have the access info for the sub-object >>>> q->array, we can surely decide the size of the sub-object q->array, but we >>>> still cannot >>>> decide the whole object that is pointed by q (the same reason as above), >>>> right? >>> >>> It's tricky, I mean we could assume p to be a valid object due to the >>> dereference and hence assume that q->foo is also valid and that there's at >>> least sizeof(*q) + q->foo * sizeof (q->array) bytes available. The >>> question then is whether q could be pointing to an element of an array of >>> `struct annotated`. Could we ever have a valid array of such structs that >>> have a flex array at the end? Wouldn't it always be a single object? >>> >>> In fact for all pointers to such structs with a flex array at the end, >>> could we always assume that it is a single object and never part of an >>> array, and hence return sizeof()? >>> >>> Thanks, >>> Sid >> > > > -- > Kees Cook