https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92312
Bug ID: 92312 Summary: bogus -Wstringop-overflow storing into a trailing array backed by larger buffer Product: gcc Version: 10.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: middle-end Assignee: unassigned at gcc dot gnu.org Reporter: msebor at gcc dot gnu.org Target Milestone: --- GCC 10.0 issues a -Wstringop-overflow warning for stores into trailing arrays that are backed by a large enough buffer to act as larger "flexible array members." This is a safe idiom that should probably be allowed without a warning. It's used for example in the Linux kernel where it triggers the following warning (and a few others): drivers/input/mouse/cyapa_gen5.c:1559:34: warning: writing 1 byte into a region of size 0 [-Wstringop-overflow=] 1559 | app_cmd_head->parameter_data[0] = GEN5_PARAMETER_DISABLE_PIP_REPORT; | ^ drivers/input/mouse/cyapa_gen5.c: In function ‘cyapa_gen5_set_power_mode’: drivers/input/mouse/cyapa_gen5.c:317:5: note: at offset 0 to object ‘parameter_data’ with size 0 declared here 317 | u8 parameter_data[0]; /* Parameter data variable based on cmd_code */ | ^~~~~~~~~~~~~~ A small test case is below. $ cat z.c && gcc -O2 -S -Wall -fdump-tree-strlen=/dev/stdout z.c struct S0 { char a, b[0]; }; void f0 (void*); void f1 (void) { char a[3]; struct S0 *p = (struct S0*)a; p->a = 0; __builtin_memset (p->b, 0, 2); // ok f0 (p); } void f2 (void) { char a[3]; struct S0 *p = (struct S0*)a; p->a = 0; p->b[0] = 0; p->b[1] = 1; // warning f0 (p); } struct Sx { char a, b[]; }; void f3 (void) { char a[3]; struct Sx *p = (struct Sx*)a; p->a = 0; p->b[0] = 0; p->b[1] = 1; // warning f0 (p); } ;; Function f1 (f1, funcdef_no=0, decl_uid=1935, cgraph_uid=1, symbol_order=0) ;; 1 loops found ;; ;; Loop 0 ;; header 0, latch 1 ;; depth 0, outer -1 ;; nodes: 0 1 2 ;; 2 succs { 1 } f1 () { char a[3]; <bb 2> [local count: 1073741824]: MEM[(struct S0 *)&a].a = 0; __builtin_memset (&MEM <char[0:]> [(void *)&a + 1B], 0, 2); f0 (&a); a ={v} {CLOBBER}; return; } ;; Function f2 (f2, funcdef_no=1, decl_uid=1940, cgraph_uid=2, symbol_order=1) ;; 1 loops found ;; ;; Loop 0 ;; header 0, latch 1 ;; depth 0, outer -1 ;; nodes: 0 1 2 ;; 2 succs { 1 } z.c: In function ‘f2’: z.c:19:11: warning: writing 1 byte into a region of size 0 [-Wstringop-overflow=] 19 | p->b[0] = 0; | ~~~~~~~~^~~ z.c:1:21: note: destination object declared here 1 | struct S0 { char a, b[0]; }; | ^ z.c:20:11: warning: writing 1 byte into a region of size 0 [-Wstringop-overflow=] 20 | p->b[1] = 1; // warning | ~~~~~~~~^~~ z.c:1:21: note: destination object declared here 1 | struct S0 { char a, b[0]; }; | ^ f2 () { char a[3]; <bb 2> [local count: 1073741824]: MEM[(struct S0 *)&a].a = 0; MEM[(struct S0 *)&a].b[0] = 0; MEM[(struct S0 *)&a].b[1] = 1; f0 (&a); a ={v} {CLOBBER}; return; } ;; Function f3 (f3, funcdef_no=2, decl_uid=1948, cgraph_uid=3, symbol_order=2) ;; 1 loops found ;; ;; Loop 0 ;; header 0, latch 1 ;; depth 0, outer -1 ;; nodes: 0 1 2 ;; 2 succs { 1 } z.c: In function ‘f3’: z.c:31:11: warning: writing 1 byte into a region of size 0 [-Wstringop-overflow=] 31 | p->b[0] = 0; | ~~~~~~~~^~~ z.c:24:21: note: destination object declared here 24 | struct Sx { char a, b[]; }; | ^ z.c:32:11: warning: writing 1 byte into a region of size 0 [-Wstringop-overflow=] 32 | p->b[1] = 1; // warning | ~~~~~~~~^~~ z.c:24:21: note: destination object declared here 24 | struct Sx { char a, b[]; }; | ^ f3 () { char a[3]; <bb 2> [local count: 1073741824]: MEM[(struct Sx *)&a].a = 0; MEM[(struct Sx *)&a].b[0] = 0; MEM[(struct Sx *)&a].b[1] = 1; f0 (&a); a ={v} {CLOBBER}; return; }