This is the mail archive of the
gcc-bugs@gcc.gnu.org
mailing list for the GCC project.
[Bug c/63303] New: Pointer subtraction is broken when using -fsanitize=undefined
- From: "mikulas at artax dot karlin.mff.cuni.cz" <gcc-bugzilla at gcc dot gnu dot org>
- To: gcc-bugs at gcc dot gnu dot org
- Date: Fri, 19 Sep 2014 01:42:05 +0000
- Subject: [Bug c/63303] New: Pointer subtraction is broken when using -fsanitize=undefined
- Auto-submitted: auto-generated
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;
}