https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77622
Bug ID: 77622 Summary: __builtin_object_size incorrect for an out-of-bounds pointer prior to destination object Product: gcc Version: 7.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: --- When the __builtin_object_size intrinsic is passed a pointer that's derived from the address of an object but points past its end it returns zero in all types. This makes it possible for _FORTIFY_SOURCE to detect writing past the end of the destination buffer. However, when the function is passed a similarly derived pointer that points before the beginning of the object it instead returns a positive value, preventing GCC from emitting the checking calls, and allowing buffer overflows to go undetected. The test case below shows these two cases. $ cat x.c && /build/gcc-trunk-git/gcc/xgcc -B /build/gcc-trunk-git/gcc -O2 -Wall -Wextra -Wpedantic -fdump-tree-optimized=/dev/stdout x.c && ./a.out #define bos(dest) __builtin_object_size (dest, 0) #define memcpy(dest, src, n) \ __builtin___memcpy_chk (dest, src, n, bos (dest)) #define P(x) \ __builtin_printf ("%2zd %2zd %2zd %2zd\n", \ __builtin_object_size (x, 0), \ __builtin_object_size (x, 1), \ __builtin_object_size (x, 2), \ __builtin_object_size (x, 3)) \ int main (void) { char d [3]; int i = 10; char *p; { p = &d[3] - i; P (p); memcpy (p, "abcdef", 5); __builtin_printf ("%.0s", p); } { p = &d[3] + i; P (p); memcpy (p, "abcdef", 5); __builtin_printf ("%.0s", p); } } x.c: In function ‘main’: x.c:19:7: warning: array subscript is below array bounds [-Warray-bounds] p = &d[3] - i; ~~^~~~~~~~~~~ x.c:19:7: warning: array subscript is below array bounds [-Warray-bounds] x.c:29:7: warning: array subscript is above array bounds [-Warray-bounds] p = &d[3] + i; ~~^~~~~~~~~~~ x.c:29:7: warning: array subscript is above array bounds [-Warray-bounds] ;; Function main (main, funcdef_no=0, decl_uid=1791, cgraph_uid=0, symbol_order=0) (executed once) main () { char d[3]; <bb 2>: __builtin_printf ("%2zd %2zd %2zd %2zd\n", 10, 10, 10, 10); __builtin_memcpy (&MEM[(void *)&d + -7B], "abcdef", 5); __builtin_printf ("%.0s", &MEM[(void *)&d + -7B]); __builtin_printf ("%2zd %2zd %2zd %2zd\n", 0, 0, 0, 0); __builtin___memcpy_chk (&MEM[(void *)&d + 13B], "abcdef", 5, 0); __builtin_printf ("%.0s", &MEM[(void *)&d + 13B]); d ={v} {CLOBBER}; return 0; } x.c:3:3: warning: call to __builtin___memcpy_chk will always overflow destination buffer __builtin___memcpy_chk (dest, src, n, bos (dest)) ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x.c:33:5: note: in expansion of macro ‘memcpy’ memcpy (p, "abcdef", 5); ^~~~~~ 10 10 10 10 0 0 0 0 *** buffer overflow detected ***: ./a.out terminated