https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63303
Bug ID: 63303 Summary: Pointer subtraction is broken when using -fsanitize=undefined Product: gcc Version: 4.9.1 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c Assignee: unassigned at gcc dot gnu.org Reporter: mikulas at artax dot karlin.mff.cuni.cz Host: x86_64-linux-gnux32 Target: x86_64-linux-gnux32 Build: x86_64-linux-gnux32 The undefined behavior sanitizer incorrectly warns about pointer subtraction. The reason is that the undefined behavior sanitizer treats pointer subtraction like subtraction of two signed integers and warns if it would result in integer overflow. However, this logic is incorrect. Subtracting of these two pointers is perfectly legal operation but it results in incorrect warning: (char *)0x90000000 - (char *)0x70000000: this bug causes spurious warnings in correct program if array spans the boundary 0x80000000 and the program subtracts pointers in this array. Subtracting these two pointers doesn't result in a warning, but it should because the resulting integer overflows: (char *)0xc0000000 - (char *)0x30000000 BTW. The sanitizer also lacks warnings when addition of a pointer and integer results in overflow. For example (char *)0xd0000000 + 0x40000000U doesn't result in a warning but it should. This is the example code, compile it with -fsanitize=undefined #include <stdio.h> #include <stddef.h> __attribute((noinline,noclone)) ptrdiff_t ptr_diff(char *p1, char *p2) { return p1 - p2; } __attribute((noinline,noclone)) void *ptr_add(char *p1, unsigned long p2) { return p1 + p2; } void *get_address(unsigned n) { return (void *)((unsigned long)n << (sizeof(void *) * 8 - 4)); } int main(void) { printf("%ld\n", (long)ptr_diff(get_address(0x9), get_address(0x7))); /* sanitizer should not warn here */ printf("%ld\n", (long)ptr_diff(get_address(0xc), get_address(0x3))); /* sanitizer should warn here */ return 0; }