Description
I have a small program (a simplification of a test function from a larger project) that slices a small array and tries to access an out-of-bounds element of the slice. Running it with cargo run --release
using the stable 1.41.0
release prints something like this (tested on macOS 10.15 and Ubuntu 19.10):
0 0 3 18446744073709551615
[1] 21065 segmentation fault cargo run --release
It looks like the resulting slice somehow has length 2**64 - 1
, so the bounds checking is omitted, which predictably results in a segfault. On 1.39.0
and 1.40.0
the very same program prints what I would expect:
0 0 3 0
thread 'main' panicked at 'index out of bounds: the len is 0 but the index is 16777216', src/main.rs:13:35
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
The problem goes away if I do any of the following:
- remove either of the two
do_test(...);
calls inmain()
; - remove the
for _ in 0..1 {
loop; - replace the
for y in 0..x {
loop withfor y in 0..1 {
; - remove the
z.extend(std::iter::repeat(0).take(x));
line or replace it withz.extend(std::iter::repeat(0).take(1));
; - replace the
for arr_ref in arr {
loop withlet arr_ref = &arr[0];
; - specify
RUSTFLAGS="-C opt-level=2"
; - specify
RUSTFLAGS="-C codegen-units=1"
.
My best guess is -C opt-level=3
enables a problematic optimization pass in LLVM, which results in miscompilation. This is corroborated by the fact that MIR (--emit mir
) and LLVM IR before optimizations (--emit llvm-ir -C no-prepopulate-passes
) is the same for both -C opt-level=2
and -C opt-level=3
.
Some additional info that might be helpful:
- I can't reproduce the problem in the Rust playground (presumably because it uses
codegen-units = 1
); - I can't reproduce the problem on Windows 10 with the same
1.41.0
release (no idea what makes it different); cargo-bisect-rustc
says the regression first happened in the2019-12-12
nightly, specifically in this commit. This seems suspicious to me, given that1.40.0
, which does not exhibit the problem, was released after this date.
I'm attaching the program inline in case the GitHub repo doesn't work (if you want to compile it without Cargo, use rustc -C opt-level=3 main.rs
):
fn do_test(x: usize) {
let arr = vec![vec![0u8; 3]];
let mut z = Vec::new();
for arr_ref in arr {
for y in 0..x {
for _ in 0..1 {
z.extend(std::iter::repeat(0).take(x));
let a = y * x;
let b = (y + 1) * x - 1;
let slice = &arr_ref[a..b];
eprintln!("{} {} {} {}", a, b, arr_ref.len(), slice.len());
eprintln!("{:?}", slice[1 << 24]);
}
}
}
}
fn main() {
do_test(1);
do_test(2);
}