Difference between compile-time and runtime float precision on 32-bit x86 without SSE can cause miscompilation leading to segfault · Issue #89885 · llvm/llvm-project (original) (raw)
The following program (based on the Rust version from here, which is based on @comex's example from a different issue; they gave an explanation of how they found it here)
#include <stdio.h> #include <stddef.h>
void print_vals(float x, size_t i, int vals_i) { printf("x=%f i=%zu vals[i]=%i\n", x, i, vals_i); }
void out_of_bounds(float x, size_t i) { printf("x=%f i=%zu vals[i]=out of bounds\n", x, i); }
void evil(int vals[300]) { float x = 0.0; size_t i = 0;
while (x != 90.0) {
// At compile time, LLVM will brute-force the loop and discover that it
// terminates after 90 iterations, with `i` always less than 300. This bounds
// check therefore gets optimised out.
if (i < 300) {
print_vals(x, i, vals[i]);
} else {
out_of_bounds(x, i);
}
x += 1.0;
// This addition is too small to have any effect on the value of `x` with
// regular `float` precision, which is what LLVM uses at compile-time.
// However, with the extra precision offered by x87 floating point registers,
// the value of `x` will change slightly, meaning it will never hit exactly
// 90.0 and therefore the loop will never terminate at runtime.
x += 2.9802322387695312e-08;
i += 2;
}
}
int main() { int vals[300]; for (int i = 0; i < 300; i++) { vals[i] = i; } evil(vals); }
compiled with clang -O3 --target=i686-unknown-linux-gnu -mno-sse code.c
will segfault at runtime. This is due to LLVM evaluating floats at standard float precision at compile-time, but outputting machine code that uses x87 extended precision at runtime. Specifically, llvm/lib/Analysis/ScalarEvolution.cpp will brute force the loop using compile-time semantics, causing the bounds check to be optimised out; however the extra precision of x87 extended precision floats will mean that the loop termination condition is never hit at runtime.
The LangRef appears to imply that the compile-time semantics are correct, so this is a bug in the x86 backend.
Related to #44218.