From d6e1a0f7a4c027bb2d3bc5fc3cc8171867c5d3ec Mon Sep 17 00:00:00 2001 From: japabu Date: Mon, 28 Sep 2026 10:06:20 +0200 Subject: [PATCH 1/3] Disable handle_basic behind the deferred-release-outlives-its-syscall issue The census instrument reds it the same way it reds handle_kill_policy, handle_transfer and kill_while_blocked: one extra live PipeWrite behind after handle churn, the last round's drop(write) still in the release queue at the second reading. A flaky test is disabled at once. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01W6rME2DoqwjcYFStYHHY4j --- issues/kernel/deferred-release-outlives-its-syscall.md | 10 ++++++++++ src/redlist.rs | 1 + 2 files changed, 11 insertions(+) diff --git a/issues/kernel/deferred-release-outlives-its-syscall.md b/issues/kernel/deferred-release-outlives-its-syscall.md index ad6132de8e..d9a448bbd8 100644 --- a/issues/kernel/deferred-release-outlives-its-syscall.md +++ b/issues/kernel/deferred-release-outlives-its-syscall.md @@ -88,6 +88,16 @@ 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 fifth 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; PR #564 does not change the tests that run with it (its +reviewer checked this). 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. Its redlist +row cites this paragraph. + ## 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 301519afaf..047078a781 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", From 6c3af992a71b4a018684ad99d623d87e0e95aae3 Mon Sep 17 00:00:00 2001 From: japabu Date: Mon, 28 Sep 2026 10:38:09 +0200 Subject: [PATCH 2/3] userdev_dma_fault runs log_origin, so handle_basic's census no longer runs in Fast userdev_dma_fault carried test_rs_handle_basic and required exit 0 after the staged DMA fault. With handle_basic disabled behind deferred-release-outlives-its-syscall, its census arm still ran there, on the actuator kernel, and a release-queue lag would have read as "the guest ran after the fault and failed" - an IOMMU fault-survival regression it is not. What userdev_dma_fault needs from its binary is a process that the machine spawns after the fault and that exits 0. log_origin is the smallest built binary that runs standalone and exits 0 with nothing staged (661104 bytes; the smaller ones panic, segfault, hold the boot for a host, or paint the screen), and it is on RUST_SKIP, so no shared-boot declaration is needed. handle_basic is no longer driven by any machine test, so its DRIVEN_AND_SHARED entry goes, as suite_split requires. The issue records the assertions handle_basic alone held, which now run in no gate, and that its exit brings them back. The review's REMOVEs are taken. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01W6rME2DoqwjcYFStYHHY4j --- .../kernel/deferred-release-outlives-its-syscall.md | 13 ++++++++----- tests/common/iommu.rs | 12 ++++++------ tests/toyos.rs | 11 +++-------- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/issues/kernel/deferred-release-outlives-its-syscall.md b/issues/kernel/deferred-release-outlives-its-syscall.md index d9a448bbd8..56abce766b 100644 --- a/issues/kernel/deferred-release-outlives-its-syscall.md +++ b/issues/kernel/deferred-release-outlives-its-syscall.md @@ -88,15 +88,18 @@ 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 fifth witness, PR #564 at `4919fbd7`.** `handle_basic` red at +**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; PR #564 does not change the tests that run with it (its -reviewer checked this). CI run 33266767478, job 99138099030 reds the same +`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. Its redlist -row cites this paragraph. +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 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..2bdbb89572 100644 --- a/tests/toyos.rs +++ b/tests/toyos.rs @@ -194,8 +194,8 @@ 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_program_line`, + // `log_stream` and `userdev_dma_fault` run it. "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 +491,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 +1660,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"]), From 87c1373f31bf9714ebf97d7004aa47a0032d4a91 Mon Sep 17 00:00:00 2001 From: japabu Date: Mon, 28 Sep 2026 12:22:25 +0200 Subject: [PATCH 3/3] Delete the log_origin comment's driver list: CARRIES already answers it, and it omits log_stream_e1000e Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01W6rME2DoqwjcYFStYHHY4j --- tests/toyos.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/toyos.rs b/tests/toyos.rs index 2bdbb89572..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`, - // `log_stream` and `userdev_dma_fault` 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`