diff --git a/issues/kernel/deferred-release-outlives-its-syscall.md b/issues/kernel/deferred-release-outlives-its-syscall.md index ad6132de8e..56abce766b 100644 --- a/issues/kernel/deferred-release-outlives-its-syscall.md +++ b/issues/kernel/deferred-release-outlives-its-syscall.md @@ -88,6 +88,19 @@ recorded mechanism through the census instrument on the hosted shard — the first sighting of this class off the dev host. Its redlist row cites this paragraph. +**A witness, PR #564 at `4919fbd7`.** `handle_basic` red at +`tests/toyos-rust-tests/src/bin/handle_basic.rs:305` — sixteen more rounds of +handle churn left one extra live `PipeWrite` behind (`[("PipeWrite", 5, 6)]`), +`PipeRead` unchanged. CI run 33266767478, job 99138099030 reds the same +assertion on `wt/toyos-wv-fs` at `b10c4daf`, green when run alone. `PipeWrite` ++1 with `PipeRead` unchanged is the last round's `drop(write)` still in the +release queue at the second census reading: this issue's defect. +While `handle_basic` is disabled, four of its assertions run in no gate at all — +a closed slot reissued at generation+1, a superset of rights refused, `dup2` +answering generation 0, then 1, and keeping it across a live replace, and a +spent slot retiring with the table exactly one slot smaller — so this issue's +exit brings them back by re-enabling it. + ## A syscall answering the wrong word, 2026-08-20 **The three witnesses above are quantities that settle. This one is not.** diff --git a/src/redlist.rs b/src/redlist.rs index 8fb71e1968..e16b0bfcaf 100644 --- a/src/redlist.rs +++ b/src/redlist.rs @@ -35,6 +35,7 @@ pub const DISABLED: &[Disabled] = &[ }, Disabled { test: "desktop_window_child", issue: "issues/kernel/desktop-window-child-freeze.md" }, Disabled { test: "doom_sound_flood", issue: "issues/audio/doom-sound-flood-played-full-scale-once.md" }, + Disabled { test: "handle_basic", issue: "issues/kernel/deferred-release-outlives-its-syscall.md" }, Disabled { test: "handle_kill_policy", issue: "issues/kernel/handle-kill-policy-census-grew-one-sharedmem-on-two-nightlies.md", diff --git a/tests/common/iommu.rs b/tests/common/iommu.rs index cb3b49349e..7a50b1ce2b 100644 --- a/tests/common/iommu.rs +++ b/tests/common/iommu.rs @@ -2053,16 +2053,16 @@ pub fn userdev_dma_fault( ) -> Result<(), String> { let _ = c_bins; // One guest binary, for the half of this test the fault line cannot say: - // that the machine still schedules, spawns, and answers. `handle_basic` - // makes and closes an object of every kind and counts the census, so a - // kernel limping after the fault fails it rather than passing vacuously. + // that the machine still schedules, spawns, and answers. `log_origin` says + // one line and exits, and asserts nothing else: a verdict that rides a + // deferred release would red here as a fault it is not. let bins: Vec<(String, Vec)> = rust_bins .iter() - .filter(|(name, _)| name == "handle_basic") + .filter(|(name, _)| name == "log_origin") .cloned() .collect(); if bins.is_empty() { - return Err("handle_basic was not built".to_string()); + return Err("log_origin was not built".to_string()); } let mut qemu = foreign_fault(test_config, &[], &bins, &USERDEV_FOREIGN)?; let log = Serial::named("boot console", qemu.boot_log().to_string()); @@ -2082,7 +2082,7 @@ pub fn userdev_dma_fault( // And the machine is running. This is the assertion the whole stage is // for: a guest that answers here is one whose scheduler, spawn path and // IPC all survived a device being refused mid-flight. - let result = qemu.run_test("test_rs_handle_basic", Duration::from_secs(60)); + let result = qemu.run_test("test_rs_log_origin", Duration::from_secs(60)); if let Some(err) = &result.error { return Err(format!( "the guest stopped answering after the fault: {err}\n{}\n{}", diff --git a/tests/toyos.rs b/tests/toyos.rs index 6981a56cc9..4cb11680e3 100644 --- a/tests/toyos.rs +++ b/tests/toyos.rs @@ -194,8 +194,7 @@ const RUST_SKIP: &[&str] = &[ // `log_program_forgery` runs it. "log_forger", // Its verdict is where its one line went — `/log`, the served log and the - // console — which only a boot of its own reads back. `log_program_line` - // and `log_stream` run it. + // console — which only a boot of its own reads back. "log_origin", // Its verdict is where its line lands among the kernel's records, which // every other binary's records would crowd. `log_program_line_after_its_records` @@ -491,11 +490,6 @@ const DRIVEN_AND_SHARED: &[&str] = &[ // The log-stream arms drive it for the kernel's `exit:` record about it, // not for anything it does: it is the cheapest process this tree starts. "empty_dir_stat", - // Its shared run is a whole handle-lifecycle gate with its own census; - // `userdev_dma_fault` drives the same binary for a different reason - // entirely — as the proof the machine still schedules and spawns after a - // device was refused at the unit — and stages nothing for it. - "handle_basic", "hierarchy_paths", "null_sink_client_exits", "nvme_home_roundtrip", @@ -1665,7 +1659,7 @@ const CARRIES: &[(&str, &[&str])] = &[ ("input_claim_absent", &["test_rs_input_absent"]), ("gpu_set_resolution", &["test_rs_gpu_set_resolution"]), ("iommu_gpu_scanout_swap", &["test_rs_gpu_scanout_swap"]), - ("userdev_dma_fault", &["test_rs_handle_basic"]), + ("userdev_dma_fault", &["test_rs_log_origin"]), ("userdev_residue_is_its_own", &["test_rs_userdev_residue"]), ("blockd_serves_partitions", &["test_rs_blockd_io"]), ("blockd_survives_its_death", &["test_rs_blockd_io"]),