From cd54555d7b7e670030ebdb7c0d8e4d2f7dc54efd Mon Sep 17 00:00:00 2001 From: Eric Curtin Date: Tue, 13 Oct 2026 09:27:39 +0100 Subject: [PATCH] test(sandbox): isolate landlock fd limit test Signed-off-by: Eric Curtin --- .../src/sandbox/linux/landlock.rs | 55 +++++++++++++------ 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/crates/openshell-sandbox/src/sandbox/linux/landlock.rs b/crates/openshell-sandbox/src/sandbox/linux/landlock.rs index 9931321bd7..5fd79d90cb 100644 --- a/crates/openshell-sandbox/src/sandbox/linux/landlock.rs +++ b/crates/openshell-sandbox/src/sandbox/linux/landlock.rs @@ -665,32 +665,53 @@ mod tests { } #[test] - fn capability_free_baseline_drops_each_root_entry_before_opening_the_next() { + fn capability_free_baseline_streams_root_entries_under_a_low_fd_limit() { + const CHILD_MARKER: &str = "OPENSHELL_LANDLOCK_FD_LIMIT_CHILD"; + const ENTRIES: u64 = 128; + const FD_LIMIT: u64 = 64; + const { assert!(ENTRIES > FD_LIMIT) }; + + if require_abi().is_err() { + return; + } + // RLIMIT_NOFILE is process-wide, so lower it in a fresh copy of the + // test binary where no other test opens descriptors. + if std::env::var_os(CHILD_MARKER).is_none() { + let status = std::process::Command::new(std::env::current_exe().unwrap()) + .args([ + "--exact", + "sandbox::linux::landlock::tests::capability_free_baseline_streams_root_entries_under_a_low_fd_limit", + "--nocapture", + ]) + .env(CHILD_MARKER, "1") + .status() + .expect("run isolated descriptor limit test"); + assert!(status.success(), "isolated descriptor limit test failed"); + return; + } + let root = tempfile::tempdir().unwrap(); - for index in 0..128 { + for index in 0..ENTRIES { std::fs::write(root.path().join(format!("entry-{index:03}")), b"public").unwrap(); } std::fs::create_dir(root.path().join(".openshell")).unwrap(); - let baseline = std::fs::read_dir("/proc/self/fd").unwrap().count(); - let mut visited = 0; - let mut peak = baseline; - for entry in capability_free_baseline_entries(root.path()).unwrap() { - let _entry = entry.unwrap(); - visited += 1; - peak = peak.max(std::fs::read_dir("/proc/self/fd").unwrap().count()); - } - - assert_eq!(visited, 128); - assert!( - peak <= baseline + 16, - "streamed baseline preparation retained too many descriptors: baseline={baseline} peak={peak}" - ); + // Retaining every entry descriptor would exhaust this limit. + let limit = rustix::process::getrlimit(rustix::process::Resource::Nofile); + rustix::process::setrlimit( + rustix::process::Resource::Nofile, + rustix::process::Rlimit { + current: Some(FD_LIMIT), + maximum: limit.maximum, + }, + ) + .unwrap(); + prepare_capability_free_baseline_at(root.path()).unwrap(); } #[test] fn capability_free_baseline_denies_alias_reads_and_path_truncation() { - if !matches!(probe_availability(), LandlockAvailability::Available { abi } if abi >= 3) { + if require_abi().is_err() { return; } let root = tempfile::tempdir().unwrap();