Repository navigation
Conversation
Re-traced each against the current tree and recorded it as an issues/ file with an exit a test can fail; no fix (each fix is its own later change). - SYS-01: sys_thread_spawn does not bound the thread entry, so a noncanonical entry faults the Ring 0 iretq and halt_all_cpus takes the machine. - SYS-02: SYS_INBOX_SETUP and SYS_NAMESPACE_OPEN install a handle, drop the process-data lock, and on a failed fallible step roll back with ops::close(...).expect(...); a sibling close makes that expect panic. - CON-01: sys_chdir holds the match-scrutinee VFS guard into with_process_data (VFS then process-data), the inverse of sys_open (process-data then VFS): a two-thread deadlock. - MEM-01: sys_shm_map clones the Arc and maps with no retirement check while on_zero_handles tears down exactly once, publishing a mapping after the only teardown. A red test cannot be committed: a clean-tree red breaks --ci host and the merge queue. The red tests are patches in the pull request; the three races need a new kernel actuator to stage, a separate change past this branch's fence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RvnWQFcMuGqTHYhvSnTe8A
The SYS-01 test, as run and reversedApplied to diff --git a/tests/toyos-rust-tests/src/bin/spawn_noncanonical_entry.rs b/tests/toyos-rust-tests/src/bin/spawn_noncanonical_entry.rs
new file mode 100644
index 000000000..9bbb770b4
--- /dev/null
+++ b/tests/toyos-rust-tests/src/bin/spawn_noncanonical_entry.rs
@@ -0,0 +1,34 @@
+//! A thread entry that is no canonical address is refused, or ends its own
+//! process at most: the machine outlives it.
+
+use toyos_abi::syscall::{self, MmapFlags, MmapProt, SyscallError};
+
+const PAGE_2M: usize = 2 * 1024 * 1024;
+
+/// Canonical under neither 48-bit nor 57-bit addressing.
+const NONCANONICAL: u64 = 0x0100_0000_0000_0000;
+
+fn main() {
+ // SAFETY: a fresh anonymous mapping nothing else names.
+ let stack = unsafe {
+ syscall::mmap(
+ core::ptr::null_mut(),
+ PAGE_2M,
+ MmapProt::READ | MmapProt::WRITE,
+ MmapFlags::ANONYMOUS | MmapFlags::PRIVATE,
+ )
+ };
+ assert!(!stack.is_null(), "no memory for the thread's stack");
+ let base = stack as u64;
+ // SAFETY: the stack is the mapping above; the entry is the input under test.
+ let answer = unsafe { syscall::thread_spawn(NONCANONICAL, base + PAGE_2M as u64, 0, base) };
+ if SyscallError::from_u64(answer).is_none() {
+ // Accepted: the thread's first return to Ring 3 is where the harm is, so wait it out before judging.
+ syscall::thread_join(answer);
+ }
+ assert_eq!(
+ SyscallError::from_u64(answer),
+ Some(SyscallError::InvalidArgument),
+ "a thread entry that is no canonical address was not refused",
+ );
+}
diff --git a/tests/toyos.rs b/tests/toyos.rs
index 996ed86de..555db546f 100644
--- a/tests/toyos.rs
+++ b/tests/toyos.rs
@@ -68,6 +68,9 @@ const ACTUATOR_KERNEL: &[&str] = toyos_build::build::TEST_KERNEL;
// Rust helper binaries that are spawned by tests, not tests themselves.
const RUST_SKIP: &[&str] = &[
+ // It asks for a thread at an entry no CPU can return to: `thread_entry_noncanonical`
+ // runs it on a boot of its own, since a kernel that takes the entry takes the machine.
+ "spawn_noncanonical_entry",
// **Its exit code is a measurement, not a verdict**, and the shared block
// judges every member on `exit=0` alone — so it would red on every boot
// that measured anything. It also needs the real-time band, which only
@@ -245,6 +248,9 @@ const SCREEN_TESTS: &[(&str, qemu::Profile)] = &[
/// The tests whose machine shape *is* the test, each on a boot of its own.
/// `run_machine_test` dispatches them.
const MACHINE_TESTS: &[&str] = &[
+ // The return to a thread's entry is the CPU's own instruction: no host test
+ // executes it, and the T14's rows share their boot with what it would take down.
+ "thread_entry_noncanonical",
// Whether QEMU's virtio functions negotiated `VIRTIO_F_ACCESS_PLATFORM`
// behind its emulated VT-d unit and without one: no shipped machine has a
// virtio function, so only a QEMU machine can be asked.
@@ -2711,6 +2717,21 @@ fn run_machine_test(name: &str, test_config: &Path) -> Result<(), String> {
"iommu_virtio_platform" => common::iommu::iommu_virtio_platform(test_config),
"nested_nmi_is_loud" => faults::nested_nmi_is_loud(test_config),
"machine_shutdown" => power::machine_shutdown(test_config),
+ "thread_entry_noncanonical" => {
+ let name = "spawn_noncanonical_entry";
+ let tests = compile::repo_root().join("tests/toyos-rust-tests");
+ let bin = (name.to_string(), qemu::build_toyos_bin(qemu::SUITE_ARCH, &tests, name));
+ let mut qemu = QemuInstance::boot_with_options(test_config, &[], &[bin], BootOptions::default());
+ let result = qemu.run_test(&format!("test_rs_{name}"), Duration::from_secs(60));
+ match (result.exit_code, &result.error) {
+ (Some(0), None) => Ok(()),
+ (code, error) => Err(format!(
+ "exit {code:?}: {}\n{}",
+ error.as_ref().map_or(String::new(), ToString::to_string),
+ result.stdout
+ )),
+ }
+ }
"acpi_power_button" => power::acpi_power_button(test_config),
"machine_shutdown_short_stop" => power::machine_shutdown_short_stop(test_config),
other => Err(format!("unknown machine test {other}")),The three racesNo test is written for these: each needs an actuator the kernel does not have, named in its issue, and a test against a hook that does not exist is not a patch anyone can run. |
…s under TCG, and three races are traced only The one sequential finding was run as a guest test under QEMU TCG. The spawn is accepted and the thread's fault arrives with a Ring 3 frame (cs=0x23), so the kernel ends that process alone and the machine lives: the halt the audit claims did not happen on this instrument. Intel's SDM puts the fault in IRET itself, in Ring 0, and only the T14 can be asked; the issue is renamed to what was measured, becomes a finding, and its exit is that metal row. The three interleavings say "traced, not executed" and name the actuator each needs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RvnWQFcMuGqTHYhvSnTe8A
Review, round 1, of
|
…-entry record leaves for the branch that fixes it - The thread-entry record is deleted here. `wt/toyos-threadentry` carries it in its first commit and deletes it with the refusal it asks for, so this branch records the three races and nothing else. - Each of the three names an owner. - The IPC record gives the install, the push and the rollback to `connect_through`, which `sys_namespace_open` calls. - "Traced, not executed." is a paragraph of its own before each exit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RvnWQFcMuGqTHYhvSnTe8A
Answer to review round 1, at
|
|
Closed without landing: each of the three races it records is now carried, with its issue file, by the pull request that fixes it and deletes it: #758 (the IPC setup rollback), #759 (chdir's lock order) and #762 (the shm map against the last close). The thread-entry issue went to #757 the same way. The review's reading of the three traces stands as the record that each held on |
Three races an outside audit argued from reading the kernel at
b432ed21c, each traced again in the tree and executed by nobody. Nothing is fixed. The diff againstmainis three files underissues/.Head
e34a45e99, onmain017451624.Verdicts
REPRODUCED would mean executed; none is. Each is filed
kind: defectwith one paragraph, "Traced, not executed.", that says what ran, and each names an owner.T001-SYS-02, IPC setup rollback: TRACED, NOT EXECUTED.
sys_inbox_setup(kernel/src/syscall/ipc.rs:466) andconnect_through(:265), whichsys_namespace_opencalls, close a handle withexpectafter dropping the lock it was installed under.issues/an-ipc-setup-rollback-panics-when-a-sibling-closed-its-new-handle.mdT001-CON-01, chdir against open: TRACED, NOT EXECUTED.
kernel/src/syscall/fs.rs:84keeps its scrutinee's VFS guard acrosswith_process_data;:35withkernel/src/object/ops.rstakes the two the other way.issues/chdir-holds-the-vfs-lock-into-the-process-data-lock-open-takes-the-other-way.mdT001-MEM-01, shm map against the last close: TRACED, NOT EXECUTED.
kernel/src/syscall/ipc.rs:361clones the object and drops the lock;kernel/src/object/shm.rs:132maps with no retirement check,:172tears down once,:186asserts.issues/an-shm-map-racing-the-last-close-publishes-a-mapping-after-teardown.mdNone is already recorded:
issues/deferred-release-outlives-its-syscall.mdis a late release that loses nothing, andissues/a-user-copy-demand-pages-under-whatever-its-caller-holds.mdis another pair of locks.The fourth finding is not here
The audit's T001-SYS-01, a thread entry
SYS_THREAD_SPAWNnever checks, was recorded by this branch throughf34839395and is deleted at this head. Draft #757 (wt/toyos-threadentry) carries that issue file in its first commit,3f4112a88, and deletes it with the refusal the review of this branch asked its exit to be. Its record, its test and its measurement are #757's. The first comment here still holds the test as a patch, and its run on0c9ff75e7exited 1 under TCG, the spawn accepted and the process ended alone.Why three were not executed
No model reaches them:
kernel/loom/src/lib.rstakes some twenty kernel files by#[path], and none is the handle table, the object layer or these syscalls;kernel/pure/proclife/interleave.rsmodels the process table. A guest test needs each window held open, and noSYS_DEBUGaction does that for these three paths; a new one is kernel source, outside this branch's fence. Each issue names the actuator it needs. No test that loops for a race was written.Gates
cargo run -- --ci hostate34a45e99: exit 0,[ci] Host: 77 step(s), all green.issues/alone.Unsure
kind: defecton a reading nobody has executed.issues/a-panic-is-never-an-accident.md, whose subject itsexpectis; the other two name the code that owns the window.🤖 Generated with Claude Code
https://claude.ai/code/session_01RvnWQFcMuGqTHYhvSnTe8A