A gcc-10 snapshot from earlier this month (dated 2020-04-13) produced a -Wstringop-overflow warning that made no sense when building the Linux kernel. I reduced it to a small test case https://godbolt.org/z/NyjxvH struct a { char b[50]; }; struct c { short action; struct a d; }; struct f { short command; struct c e; }; void i(struct f *f, int *g, unsigned h) { struct c *j = &f->e; j->action = 0; __builtin_memcpy(&j->d.b[h], g, 16); } $ x86_64-linux-gcc -O2 -Wall -fsanitize=thread test.c -c test.c:In function 'i': test.c:15:3: warning: writing 16 bytes into a region of size 0 [-Wstringop-overflow=] 15 | __builtin_memcpy(&j->d.b[h], g, 16); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ test.c:5:9: note: at offset 0 to object 'action' with size 2 declared here 5 | short action; | ^~~~~~
Confirmed, started with r10-5451-gef29b12cfbb4979a.
I ran into another file that triggered this problem, reducing that one gave me a slightly simpler test case: struct a { char b[8]; }; struct e { unsigned c; struct a d[2]; }; void i(struct e *e, void *g) { struct e *f = e + 1; __builtin_memcpy(f->d[f->c].b, g, 1); }
The warning is issued based on the last MEM_REF in the IL below which stores 16 bytes to the address _1. The address is computed by adding (h + 2) to _12, the address of the action member on type int. The middle-end treats the address of a member plus its size as interchangeable for the address of the next member, while the warning relies on the two being distinct (it's invalid to access a member using a pointer to another). If, instead of using &MEM[(struct c *)f_2(D) + 2B].action, it used the equivalent but strictly (according to C/C++ rules) valid &MEM[(short *)f_2(D) + 2B], i.e., the address of the enclosing struct, this wouldn't be a problem because all stores at that address are valid up to the size of the struct and the warning interprets that as the store to the member that begins at that offset. Of course, referencing the member to which the store applies would be ideal. A similar false positive with the same root cause was reported in pr94655 so resolving as a dupe. <bb 2> [local count: 1073741824]: _12 = &MEM[(struct c *)f_2(D) + 2B].action; <<< address of f->c.e.action .ASAN_CHECK (7, _12, 2, 2); MEM[(struct c *)f_2(D) + 2B].action = 0; _9 = (sizetype) h_5(D); _10 = _9 + 2; _1 = _12 + _10; .ASAN_CHECK (4, g_6(D), 16, 1); _7 = MEM <__int128 unsigned> [(char * {ref-all})g_6(D)]; .ASAN_CHECK (5, _1, 16, 1); MEM <__int128 unsigned> [(char * {ref-all})_1] = _7; <<< store at f->c.e.action + h that's bigger than size of action Incidentally, replacing the '__builtin_memcpy(&j->d.b[h], g, 16)' call with the equivalent '__builtin_memcpy(j->d.b + h, g, 16)' avoids the waning because the IL emitted in that case corresponds to valid code (as far as the warning is concerned); see below. It also implies that these differences in the IL are incidental rather than a deliberate feature and could with some effort be avoided. <bb 2> [local count: 1073741824]: _11 = &MEM[(struct c *)f_4(D) + 2B].action; .ASAN_CHECK (7, _11, 2, 2); MEM[(struct c *)f_4(D) + 2B].action = 0; _1 = &MEM[(struct c *)f_4(D) + 2B].d.b; <<< address of f->c.d.b _2 = (sizetype) h_7(D); _3 = _1 + _2; .ASAN_CHECK (4, g_8(D), 16, 1); _9 = MEM <__int128 unsigned> [(char * {ref-all})g_8(D)]; .ASAN_CHECK (5, _3, 16, 1); MEM <__int128 unsigned> [(char * {ref-all})_3] = _9; <<< in-bounds store at f->c.d.b + h *** This bug has been marked as a duplicate of bug 94655 ***