#![cfg(all(target_os = "linux", feature = "fault-injection"))]
use std::fs::File;
use std::os::fd::FromRawFd;
use std::os::unix::process::ExitStatusExt;
use std::sync::Arc;
use std::time::{Duration, Instant};
use rustfs_uring::UringDriver;
fn os_pipe() -> (Arc<File>, File) {
let mut fds = [0i32; 2];
let rc = unsafe { libc::pipe(fds.as_mut_ptr()) };
assert_eq!(rc, 0, "pipe(2) failed");
let read = unsafe { File::from_raw_fd(fds[0]) };
let write = unsafe { File::from_raw_fd(fds[1]) };
(Arc::new(read), write)
}
fn wait_until(deadline: Duration, mut cond: impl FnMut() -> bool) -> bool {
let start = Instant::now();
while start.elapsed() < deadline {
if cond() {
return true;
}
std::thread::sleep(Duration::from_millis(5));
}
cond()
}
const ABORT_CHILD_ENV: &str = "RUSTFS_URING_FAULT_ABORT_CHILD";
#[test]
fn driver_panic_aborts_instead_of_freeing_in_flight_buffers() {
if std::env::var_os(ABORT_CHILD_ENV).is_some() {
run_abort_child();
eprintln!("child: abort barrier did NOT fire within the wait window — safety regression");
std::process::exit(0);
}
match UringDriver::probe_and_start(64) {
Ok(d) => {
let _ = d.shutdown();
}
Err(e) => {
assert!(
e.is_expected_restriction(),
"probe failed OUTSIDE the expected restriction errno class: {e:?}"
);
eprintln!("SKIP driver_panic_aborts_instead_of_freeing_in_flight_buffers: restricted environment ({e:?})");
return;
}
}
let exe = std::env::current_exe().expect("locate test binary");
let status = std::process::Command::new(exe)
.args([
"--exact",
"driver_panic_aborts_instead_of_freeing_in_flight_buffers",
"--nocapture",
"--test-threads=1",
])
.env(ABORT_CHILD_ENV, "1")
.status()
.expect("spawn abort child");
assert_eq!(
status.signal(),
Some(libc::SIGABRT),
"a driver-thread panic must abort the process (leak over UAF), got {status:?}"
);
}
fn run_abort_child() {
let driver = match UringDriver::probe_and_start(64) {
Ok(d) => d,
Err(e) => {
eprintln!("SKIP abort child: restricted environment ({e:?})");
std::process::exit(0);
}
};
let (pipe_read, pipe_write) = os_pipe();
let _handle = driver.read_current(Arc::clone(&pipe_read), 4096).without_cancel_on_drop();
assert!(
wait_until(Duration::from_secs(2), || driver.stats().in_flight == 1),
"read never reached in-flight state"
);
driver.test_inject_panic();
std::thread::sleep(Duration::from_secs(3));
drop(pipe_write);
}
#[test]
fn bounded_drain_bails_out_and_leaks_on_a_stuck_op() {
unsafe {
std::env::set_var("RUSTFS_URING_FAULT_STUCK_DRAIN", "1");
std::env::set_var("RUSTFS_URING_FAULT_DRAIN_TIMEOUT_MS", "400");
}
let driver = match UringDriver::probe_and_start(64) {
Ok(d) => d,
Err(e) => {
clear_stuck_env();
assert!(
e.is_expected_restriction(),
"probe failed OUTSIDE the expected restriction errno class: {e:?}"
);
eprintln!("SKIP bounded_drain_bails_out_and_leaks_on_a_stuck_op: restricted environment ({e:?})");
return;
}
};
let (pipe_read, pipe_write) = os_pipe();
let _handle = driver.read_current(Arc::clone(&pipe_read), 4096).without_cancel_on_drop();
assert!(
wait_until(Duration::from_secs(2), || driver.stats().in_flight == 1),
"read never reached in-flight state"
);
let start = Instant::now();
let snap = driver.shutdown();
let elapsed = start.elapsed();
clear_stuck_env();
assert!(
elapsed >= Duration::from_millis(300),
"shutdown returned before the bounded-drain deadline ({elapsed:?}) — the timeout path was not taken"
);
assert!(
elapsed < Duration::from_secs(3),
"shutdown took {elapsed:?} — the shortened fault drain-timeout was not honored (real 5 s path?)"
);
assert!(
snap.in_flight >= 1,
"the stuck op should still count as in flight after the leak-over-UAF bailout: {snap:?}"
);
drop(pipe_write);
}
#[test]
fn stranded_handle_errors_after_bounded_drain_bailout() {
unsafe {
std::env::set_var("RUSTFS_URING_FAULT_STUCK_DRAIN", "1");
std::env::set_var("RUSTFS_URING_FAULT_DRAIN_TIMEOUT_MS", "400");
}
let driver = match UringDriver::probe_and_start(64) {
Ok(d) => d,
Err(e) => {
clear_stuck_env();
assert!(
e.is_expected_restriction(),
"probe failed OUTSIDE the expected restriction errno class: {e:?}"
);
eprintln!("SKIP stranded_handle_errors_after_bounded_drain_bailout: restricted environment ({e:?})");
return;
}
};
let (pipe_read, pipe_write) = os_pipe();
let handle = driver.read_current(Arc::clone(&pipe_read), 4096).without_cancel_on_drop();
assert!(
wait_until(Duration::from_secs(2), || driver.stats().in_flight == 1),
"read never reached in-flight state"
);
let snap = driver.shutdown();
clear_stuck_env();
assert!(
snap.in_flight >= 1,
"the stuck op should still count as in flight after the leak-over-UAF bailout: {snap:?}"
);
let rt = tokio::runtime::Builder::new_current_thread()
.enable_time()
.build()
.expect("build current-thread runtime");
match rt.block_on(async { tokio::time::timeout(Duration::from_secs(2), handle).await }) {
Ok(Err(_)) => {} Ok(Ok(_)) => panic!("stranded handle unexpectedly succeeded on a stuck op"),
Err(_) => panic!("stranded handle HUNG after bounded-drain bailout (rustfs/backlog#1161 regression)"),
}
drop(pipe_write);
}
fn clear_stuck_env() {
unsafe {
std::env::remove_var("RUSTFS_URING_FAULT_STUCK_DRAIN");
std::env::remove_var("RUSTFS_URING_FAULT_DRAIN_TIMEOUT_MS");
}
}
#[test]
fn probe_drain_failure_leaks_and_degrades() {
unsafe {
std::env::set_var("RUSTFS_URING_FAULT_PROBE_DRAIN", "1");
}
let result = UringDriver::probe_and_start(64);
unsafe {
std::env::remove_var("RUSTFS_URING_FAULT_PROBE_DRAIN");
}
match result {
Ok(_) => panic!("probe must fail when the drain is forced to error"),
Err(e) if e.is_expected_restriction() => {
eprintln!("SKIP probe_drain_failure_leaks_and_degrades: restricted environment ({e:?})");
}
Err(e) => {
assert!(
!e.is_expected_restriction(),
"forced probe-drain failure must not look like an environment restriction: {e:?}"
);
let driver = UringDriver::probe_and_start(64).expect("a normal probe after a forced-fault probe must still work");
let _ = driver.shutdown();
}
}
}
#[test]
fn spawn_failure_degrades_instead_of_panicking() {
unsafe {
std::env::set_var("RUSTFS_URING_FAULT_SPAWN", "1");
}
let result = UringDriver::probe_and_start_sharded(64, 3);
unsafe {
std::env::remove_var("RUSTFS_URING_FAULT_SPAWN");
}
match result {
Ok(_) => panic!("driver started despite the forced spawn failure"),
Err(e) if e.is_expected_restriction() => {
eprintln!("SKIP spawn_failure_degrades_instead_of_panicking: restricted environment ({e:?})");
}
Err(e) => {
assert!(
!e.is_expected_restriction(),
"forced spawn EAGAIN must not look like an environment restriction: {e:?}"
);
let driver =
UringDriver::probe_and_start_sharded(64, 2).expect("a normal probe after a forced spawn failure must still work");
let _ = driver.shutdown();
}
}
}