kernel: shared-fate review fixes — lock the shm/dma walks, contract edges

The adversarial review of the branch confirmed the big one: the shm/DMA
page-table walks and their pmm/heap calls ran outside the big kernel lock —
pre-existing, but fatal once the per-space cursors invited sibling threads to
race them (two concurrent creates could orphan a page table: one thread's
region silently unmapped, the frame leaked — a plausible root for the
long-standing intermittent AP ring-3 fault at the shm base). All three paths
now follow the mmap discipline: allocation, object build, record, and handle
under one lock hold with full rollback; the map itself per-page under brief
holds; dma_free's translate/unmap/free per-page likewise.

Contract edges from the same review: thread_spawn into a dying group returns
-ESRCH (was generic -1); process_kill during the condemned window answers
from the latch's stashed supervisor (0 or -EPERM, was -ESRCH once the leader
slot was reaped); exit derives the group reason from its own argument rather
than the racy exit_code global; a worker's thread_exit no longer overwrites a
concurrent group-kill stamp; checkGroupDead now asserts exactly-one
notification via the drained ring. Full suite: 100/100.
This commit is contained in:
Daniel Samson
2026-07-22 11:23:32 +01:00
parent 2bc2a0d70d
commit c8191570e1
4 changed files with 160 additions and 61 deletions
+28
View File
@@ -359,6 +359,34 @@ pub fn groupDyingLocked(root: u64) bool {
return false;
}
/// The stashed supervisor of `root`'s DYING group, or null if the group is not
/// dying. Answers the process_kill authority question during the condemned
/// window, when the leader's task slot may already be reaped. Lock held.
pub fn groupSupervisorLocked(root: u64) ?u32 {
for (&address_space_refs) |*entry| {
if (entry.count != 0 and entry.root == root) {
return if (entry.dying) entry.group_supervisor else null;
}
}
return null;
}
/// Undo a `recordSpaceMappingLocked` — the rollback half for a caller whose
/// later step failed. Removes one matching slot; the caller drops the reference
/// it had transferred. Lock held.
pub fn removeSpaceMappingLocked(root: u64, object: *anyopaque) void {
for (&address_space_refs) |*entry| {
if (entry.count == 0 or entry.root != root) continue;
for (&entry.mappings) |*slot| {
if (slot.* == object) {
slot.* = null;
return;
}
}
return;
}
}
/// The whole static task pool, for process.zig's group fan-out — which must scan
/// members under the lock it already holds. Slots may be `.free`/`.reaping`;
/// callers filter by state and must not hold pointers past the lock.