Created attachment 47176 [details] testcase This code: typedef struct { char cs[256]; } inner_small_struct; typedef struct { char cl[512]; } inner_large_struct; typedef union { inner_large_struct large; inner_small_struct small; } inner_union; typedef struct { int y; inner_union inner; } outer_struct; typedef struct { int x; char s[]; } flexarr_struct; char *t1(outer_struct *p, char str[240]) { flexarr_struct *l = (flexarr_struct *) ((char *) p + sizeof(*p) - (sizeof(inner_large_struct) - sizeof(inner_small_struct))); __builtin_strcpy(l->s, str); return l->s; } warns with trunk@277817 like that: > gcc-trunk -c -O2 -W -Wall -Warray-bounds=1 testcase.c testcase.c: In function 't1': testcase.c:28:2: warning: '__builtin_strcpy' offset 264 from the object at 'p' is out of the bounds of referenced subobject 's' with type 'char[0]' at offset 264 [\-Warray-bounds=\] 28 | __builtin_strcpy(l->s, str); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~ testcase.c:22:7: note: subobject 's' declared here 22 | char s[]; | ^ Since gcc already knows about 'p' and the offset, it should also consider sizeof(*p) when deciding to warn. Otherwise it's unfortunate that a flexible array (compared to a size 1 array s[1]) suppresses UBSAN warnings, but -Warray-bounds now warns.
The warning was introduced in r275981 which was an enhancement to detect more instances of inadvertent accesses past the end of struct members. It might be possible to relax the warning to handle some of these cases but I'm not convinced it's a good idea or worth the trouble for code that does these sorts of type-punning tricks. I suggest to rewrite the code in a more straightforward way and without the casts, e.g., like so: char *t1 (outer_struct *p, char str[240]) { char *q = (char*)p + sizeof *p - (sizeof(inner_large_struct) - sizeof(inner_small_struct)) + offsetof (flexarr_struct, s); __builtin_strcpy (q, str); return q; } When starting with a pointer to the the enclosing object GCC treats as valid accesses to the whole object if it's known (or to the largest array of such objects when it isn't). Otherwise, only accesses to the same member are considered valid when using a pointer derived from the address of that member.