This code writes a value through a pointer if one array is one past the end of another array.
#include <stdio.h>
#include <inttypes.h>
extern int first[], second[];
#define ADDR_AFTER(ptr) ((uintptr_t)((ptr) + 1))
int test(int *an_int) {
*second = 1;
if (ADDR_AFTER(first) == (uintptr_t)an_int) {
// ubsan does not like this.
*an_int = 2;
}
return *second;
}
int first[1] = {0}, second[1] = {0};
int main() {
if (ADDR_AFTER(first) == (uintptr_t)second) {
printf("test: %d\n", test(second));
printf("x: %d y: %d\n", *first, *second);
}
}
At no point do I directly compare two pointers to different objects (because I convert them to uintptr_t
first). I create a pointer to one-past-the-end of an array (which is legal), but I never dereference that pointer. As far as I can tell this should either print nothing, or print:
test: 2 x: 0 y: 2
Which is printed on Clang when optimization is -O1
or lower. At -O2
, however, it prints:
test: 1 x: 0 y: 2
With -O2 -fsanitize=undefined
it prints the this to stdout:
test: 2 x: 0 y: 2
and the following to stderr:
runtime error: store to address 0x000000cd9efc with insufficient space for an object of type 'int' 0x000000cd9efc: note: pointer points here 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
with a reference to the assignment to an_int
in test
. Is this actually undefined behavior, or is this a bug in Clang?
There's nothing invalid about your code, the compiler is wrong. If you remove the unnecessary ADDR_AFTER check in test()
, the code runs as expected with no UBSan error. If you run it with optimization enabled and without UBSan, you get the wrong output (test=1, should be 2).
Something about the ADDR_AFTER(first) == (uintptr_t)an_int
code inside test()
makes Clang do the wrong thing when compiling with -O2
.
I tested with Apple clang version 11.0.3 (clang-1103.0.32.62)
but it looks like Clang 13 and current trunk also have the bug: https://godbolt.org/z/s83ncTsbf - if you change the compiler to any version of GCC you'll see it can return 1 or 2 from main()
, while Clang always returns 1 (mov eax, 1
).
You should probably file a Clang bug for this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With