> On Apr 10, 2025, at 13:43, Martin Uecker <uec...@tugraz.at> wrote:
> 
> Am Donnerstag, dem 10.04.2025 um 17:05 +0000 schrieb Qing Zhao:
>> Hi, Martin,
>> 
>> Thanks a lot for all your comments and questions, really helpful.
>> 
>> 
> 
> ...
>>> 
>>> An example I could imagine is when you memcpy
>>> the struct.   (but it is also not entirely clear why this
>>> should not be allowed to go beyond the counted_by size
>>> if the underlying storage is larger).  
>>> 
>>> Maybe you could add it when a pointer to an annotated
>>> struct is passed as parameter, but also there it is not
>>> clear to me that we might want to materialize new
>>> accesses to the struct at this point.
>> 
>> This is true too,  and this is even true for the current implementation for 
>> p->array,
>> as I checked with a small example as below:
>> 
>> [opc@qinzhao-aarch64-ol8 counted_by_whole]$ cat t2.c
>> #include <stdlib.h>
>> #include <stddef.h>
>> 
>> struct annotated {
>>  size_t count;
>>  char array[];  
>> };
>> 
>> static size_t __attribute__((__noinline__,__noipa__)) size_of (struct 
>> annotated * obj)
>> {
>>  return __builtin_dynamic_object_size (obj->array, 1);
>> }
>> 
>> int main()
>> {
>>  __builtin_printf ("the bdos whole is %ld\n", size_of (0));
>>  return 0;
>> }
>> 
>> In the above example, the parameter to the function “size_of” has pointer 
>> type to “struct annotated”,
>> However, I passed a NULL to “size_of”, is this legal C? Looks like -Wall did 
>> not issue any warning 
>> for it.
> 
> It is legal to pass a NULL pointer.  Here, the issue is
> that the builtin does not evaluate its argument, so it
> is perhaps surprising that you can get a segfault.  If
> the access it outside of the builtin, then this is not
> a problem
> 
> static size_t __attribute__((__noinline__,__noipa__)) size_of (struct 
> annotated * obj)
> {
>  char *p = obj->array;
>  return __builtin_dynamic_object_size (p, 1);
> }

The above acts the same as

static size_t __attribute__((__noinline__,__noipa__)) size_of (struct annotated 
* obj)
{
 return __builtin_dynamic_object_size (obj->array, 1);
}

Only after I changed it as:

static size_t __attribute__((__noinline__,__noipa__)) size_of (struct annotated 
* obj)
{
  char *p = obj->array;
  p[1] = 0;
  return __builtin_dynamic_object_size (p, 1);
}

I got a segmentation fault before evaluating _bdos. 
> 
> because you get the segault anyway when the first line
> is executed.

Yes. That’s right.
> 
> Maybe we need to document that the BDOS builtin requires
> obj->array  to be accessible even though it is not
> evaluated.

Yes, this needs to be at least documented. 
> 
> But I wonder whether there are other cases where
> the object-size path can walk into dead code and create
> accesses in this way. Not sure, but I found this bug though:
> https://godbolt.org/z/ejP918nW7

Thanks, will file bugs and fix them first.

thanks.

Qing
> 
> Martin
> 
>> 
>> 
>> [opc@qinzhao-aarch64-ol8 counted_by_whole]$ sh t
>> /home/opc/Install/latest-d/bin/gcc -O2 -Wall t2.c
>> the bdos whole is -1
>> 0
>> 
>> Then when I added the counted_by attribute for FAM array as:
>> 
>> [opc@qinzhao-aarch64-ol8 counted_by_whole]$ cat t2.c
>> #include <stdlib.h>
>> #include <stddef.h>
>> 
>> struct annotated {
>>  size_t count;
>>  char array[] __attribute__ ((counted_by(count)));  
>> };
>> 
>> static size_t __attribute__((__noinline__,__noipa__)) size_of (struct 
>> annotated * obj)
>> {
>>  return __builtin_dynamic_object_size (obj->array, 1);
>> }
>> 
>> int main()
>> {
>>  __builtin_printf ("the bdos whole is %ld\n", size_of (0));
>>  return 0;
>> }
>> [opc@qinzhao-aarch64-ol8 counted_by_whole]$ sh t
>> /home/opc/Install/latest-d/bin/gcc -O2 -Wall t2.c
>> t: line 13: 2944007 Segmentation fault      (core dumped) ./a.out
>> 139
>> 
>> This is because we insert the load from the &p->count for the size.
>> 
>> Qing
>> 
>>> 
>>> An alternative approach could be to just do it when
>>> such a pointer is explicitely passed to the BDOS builtin. 
>>> 
>>> Martin
>>> 
>>> 
>>>> 
>>>> Qing
>>>> 
>>>> 1 #include <stdlib.h>
>>>> 2 #include <stddef.h>
>>>> 3    
>>>> 4 struct annotated {
>>>> 5   size_t count;
>>>> 6   char array[] __attribute__((counted_by (count)));
>>>> 7 };
>>>> 8 
>>>> 9 /* compute the minimum # of bytes needed to hold a structure “annotated”,
>>>> 10    whose # of elements of “array” is COUNT.  */
>>>> 11 #define MAX(A, B) (A > B) ? (A) : (B)
>>>> 12 #define ALLOC_SIZE_ANNOTATED(COUNT) \
>>>> 13   MAX(sizeof (struct annotated), \
>>>> 14       offsetof(struct annotated, array[0]) + (COUNT) * sizeof(char))
>>>> 15 
>>>> 16 /* allocate the memory for the structure with FAM,
>>>> 17    update “count” with the # of elements “index”.  */
>>>> 18 static struct annotated * __attribute__((__noinline__)) alloc_buf (int 
>>>> index)
>>>> 19 {
>>>> 20   struct annotated *p;
>>>> 21   p = (struct annotated *) malloc (ALLOC_SIZE_ANNOTATED(index));
>>>> 22   p->count = index;
>>>> 23   return p;
>>>> 24 }
>>>> 25 
>>>> 26 static size_t __attribute__((__noinline__)) size_of (struct annotated * 
>>>> obj)
>>>> 27 {
>>>> 28   return __builtin_dynamic_object_size (obj, 1);
>>>> 29 }
>>>> 30  
>>>> 31 int main()
>>>> 32 {
>>>> 33   struct annotated *q = alloc_buf (10);
>>>> 34   __builtin_printf ("the bdos whole is %d\n", size_of (q));
>>>> 35   return 0;
>>>> 36 }


Reply via email to