View all comments
I tried this code:
#![cfg_attr(not(test), no_std)]
extern crate alloc;
use alloc::vec::Vec;
fn with(f: impl FnOnce(Vec<usize>)) {
f(Vec::new())
}
#[test]
fn repro_len_ne_unreachable() {
with(|mut v| v.resize(2, 1));
with(|v| {
if v.len() != 0 {
unreachable!();
}
});
}
I expected to see this happen: the test should pass, because each with call receives a fresh empty Vec<usize>, so the second closure should observe v.len() == 0 and never enter the unreachable!() branch.
Instead, this happened: in --profile release, the test process aborts due to allocator corruption.
Observed results:
Windows x86_64-pc-windows-msvc:
process aborts with 0xc0000374 (STATUS_HEAP_CORRUPTION)
Linux x86_64-unknown-linux-gnu (Docker rust:latest image):
free(): double free detected in tcache 2
process aborts with SIGABRT
Two nearby controls do not reproduce:
fn with_value<T>(f: impl FnOnce(Vec<T>)) {
f(Vec::new())
}
#[test]
fn control_generic_helper() {
with_value(|mut v: Vec<usize>| v.resize(2, 1));
with_value(|v: Vec<usize>| {
if v.len() != 0 {
unreachable!();
}
});
}
#[test]
fn repro_len_ne_unreachable_unchecked() {
with(|mut v| v.resize(2, 1));
with(|v| {
if v.len() != 0 {
unsafe {
core::hint::unreachable_unchecked();
}
}
});
}
Commands used:
cargo +nightly miri test repro_len_ne_unreachable -- --exact --nocapture
$env:CARGO_TARGET_DIR='target/report-repro'; cargo test --profile release repro_len_ne_unreachable -- --exact --nocapture
$env:CARGO_TARGET_DIR='target/report-control-generic'; cargo test --profile release control_generic_helper -- --exact --nocapture
$env:CARGO_TARGET_DIR='target/report-control-unchecked'; cargo test --profile release repro_len_ne_unreachable_unchecked -- --exact --nocapture
docker run --rm -v ${PWD}:/work -w /work rust:latest cargo test --profile release repro_len_ne_unreachable -- --exact --nocapture
Miri did not report UB on an earlier form of this reproducer.
Version history:
stable-1.74.1: ok
stable-1.75.0: ok
stable-1.76.0: ok
stable-1.77.0: first wrong result, panics at `unreachable!()`
stable-1.85.0: still panics at `unreachable!()`
stable-1.90.0: still panics at `unreachable!()`
stable-1.91.0: first stable with STATUS_HEAP_CORRUPTION
stable-1.92.0: STATUS_HEAP_CORRUPTION
stable-1.93.0: STATUS_HEAP_CORRUPTION
stable-1.94.0: STATUS_HEAP_CORRUPTION
nightly-2026-04-12: STATUS_HEAP_CORRUPTION
Nightly boundaries I checked:
last good nightly: nightly-2024-01-16, rustc 1.77.0-nightly (714b29a17 2024-01-15)
first bad nightly: nightly-2024-01-17, rustc 1.77.0-nightly (098d4fd74 2024-01-16), wrong panic at `unreachable!()`
earliest tested nightly with heap corruption: nightly-2024-01-18, rustc 1.77.0-nightly (6ae4cfbbb 2024-01-17)
last tested nightly with wrong panic before the later stable corruption window: nightly-2025-08-12, rustc 1.91.0-nightly (1ebbd87a6 2025-08-11)
first tested nightly with heap corruption in that window: nightly-2025-08-14, rustc 1.91.0-nightly (3672a55b7 2025-08-13)
So on stable, the bug first appears as an incorrect panic in 1.77.0 and becomes heap corruption in 1.91.0. On nightly, the first wrong result is between 2024-01-16 and 2024-01-17.
Meta
rustc --version --verbose:
rustc 1.94.0 (4a4ef493e 2026-03-02)
binary: rustc
commit-hash: 4a4ef493e3a1488c6e321570238084b38948f6db
commit-date: 2026-03-02
host: x86_64-pc-windows-msvc
release: 1.94.0
LLVM version: 21.1.8
Nightly also reproduces:
rustc 1.97.0-nightly (14196dbfa 2026-04-12)
binary: rustc
commit-hash: 14196dbfa3eb7c30195251eac092b1b86c8a2d84
commit-date: 2026-04-12
host: x86_64-pc-windows-msvc
release: 1.97.0-nightly
LLVM version: 22.1.2
Linux container reproduction:
running 1 test
free(): double free detected in tcache 2
error: test failed, to rerun pass `--lib`
Caused by:
process didn't exit successfully: `/work/target/release/deps/repro_test-9864353ff121d9c3 repro_len_ne_unreachable --exact --nocapture` (signal: 6, SIGABRT: process abort signal)
Additional stable versions checked:
rustc 1.74.1 (a28077b28 2023-12-04)
rustc 1.75.0 (82e1608df 2023-12-21)
rustc 1.76.0 (07dca489a 2024-02-04)
rustc 1.77.0 (aedd173a2 2024-03-17)
rustc 1.85.0 (4d91de4e4 2025-02-17)
rustc 1.90.0 (1159e78c4 2025-09-14)
rustc 1.91.0 (f8297e351 2025-10-28)
rustc 1.92.0 (ded5c06cf 2025-12-08)
rustc 1.93.0 (254b59607 2026-01-19)
Backtrace
I reran the reproducer with RUST_BACKTRACE=1, but there was still no Rust backtrace.
The process aborts immediately with:
running 1 test
error: test failed, to rerun pass `--lib`
Caused by:
process didn't exit successfully: `C:\Users\akrut\repro_test\target/report-backtrace\release\deps\repro_test-1f8b8a2035f2538a.exe repro_len_ne_unreachable --exact --nocapture` (exit code: 0xc0000374, STATUS_HEAP_CORRUPTION)
View all comments
I tried this code:
I expected to see this happen: the test should pass, because each
withcall receives a fresh emptyVec<usize>, so the second closure should observev.len() == 0and never enter theunreachable!()branch.Instead, this happened: in
--profile release, the test process aborts due to allocator corruption.Observed results:
Two nearby controls do not reproduce:
Commands used:
Miri did not report UB on an earlier form of this reproducer.
Version history:
Nightly boundaries I checked:
So on stable, the bug first appears as an incorrect panic in 1.77.0 and becomes heap corruption in 1.91.0. On nightly, the first wrong result is between 2024-01-16 and 2024-01-17.
Meta
rustc --version --verbose:Nightly also reproduces:
Linux container reproduction:
Additional stable versions checked:
Backtrace
Here's a miscompile in debug mode (should print 0, but prints 1):
The miscompilation occurs with
.call()and.call_mut()but not.call_once().Using
RUSTC_BOOTSTRAP=1, I found that this miscompilation was a regression from 1.68.0 (prints0) to 1.69.0 (prints1).It appears that rust is passing an arguent to the closure by passing a pointer that points to
value. The closure then mutates through the pointer. The…