summaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/03-architecture/13-vexecution-plane.md48
-rw-r--r--docs/04-implementation/03-execution-frontier.md232
-rw-r--r--docs/05-operations/05-gpubreach-audit.md95
-rw-r--r--docs/README.md1
4 files changed, 275 insertions, 101 deletions
diff --git a/docs/03-architecture/13-vexecution-plane.md b/docs/03-architecture/13-vexecution-plane.md
index 733da13..caaad01 100644
--- a/docs/03-architecture/13-vexecution-plane.md
+++ b/docs/03-architecture/13-vexecution-plane.md
@@ -1038,6 +1038,53 @@ technical barrier between the project's architecture and its promise.
---
+## m91-class I-fetch faults: capture and icache coherence (2026-10-18)
+
+The physical submit's remaining transient failure class ("m91 class" after the
+trap-decode campaign that first named the SM instruction-fetch signature) has
+two observable faces. In one, the first attempt of a mid-sequence launch
+raises SM warp exceptions ("Illegal Instruction Encoding", "Out Of Range
+Register") on garbage instruction bytes while the engine-authored release
+stays at its failure value, and a retry of the *identical* QMD and shadow
+content succeeds. In the other, the launch reports fetch success but writes
+nothing (dispatch loss). The submit retry that recovers the first face is the
+fast re-arm: the same method block is re-published on a fresh GPFIFO ring
+entry and the mode-35 lane-enable tail plus channel/TSG schedules and the
+doorbell are re-issued — no shadow re-copy, no QMD re-read, no content
+change. Identical content flipping outcome across attempts is the signature
+of stale engine-side fetch state, not a content or relocation error.
+
+Two code changes follow from that:
+
+* **FAULT-CAP (diagnostic).** When a forwarded launch's fault epoch advanced
+ across its window, the submit emits one `vgsp-m91: FAULT-CAP` line per
+ launch (capped at 20 per module-load cycle, then one suppression notice):
+ fault-epoch delta, the forwarded QMD address and first 64 B, the live
+ program pair (word 48/49 read) with its resolved engine-visible VA/PA, the
+ first 64 B at that code address, the retry decision (fast re-arm /
+ resched/fresh-entry / none) and outcome (ok vs timeout), and the newest
+ relay-decoded fault note (exceptType, chid, errString). It is inert on a
+ fault-free launch: one epoch read and one compare.
+* **icache coherence (fix, default on).** The window-0 shadow pool alternates
+ two 8-page banks by submit epoch, so the bytes at a program VA change every
+ two launches, while the captured guest QMDs carry the QMD v2.3
+ cache-control word (word 5) with the
+ `INVALIDATE_INSTRUCTION_CACHE` bit (bit 30) clear — the field the client
+ driver arms only under its forced-invalidate mode. The submit now sets
+ that bit in the forwarded QMD when the QMD carries the driver's standard
+ cache-control signature (word 5 bits 26..29 set), forcing an instruction
+ fetch at dispatch and removing the stale-line window; an unknown QMD
+ revision is never touched, and `gsp_phys_gr_icache_inv=0` restores the
+ exact pre-change QMD for a one-flip A/B. Setting the bit cannot alter
+ instruction bytes or data — it only invalidates cached fetch state — so
+ green launches keep their semantics.
+
+The next physical cycle's evidence is the FAULT-CAP lines (or their absence)
+against the four-client class: zero FAULT-CAP lines with the four clients
+green confirms the stale-fetch mechanism; FAULT-CAP content policies the
+remaining candidates (shadow-VA reuse distance, cross-cycle context state
+left by killed clients).
+
## See also
* [`01-system-overview.md`](01-system-overview.md) — principles and layer map
@@ -1057,3 +1104,4 @@ technical barrier between the project's architecture and its promise.
|---|---|
| 2026-10-08 | Created: canonical name (vGSP Execution Plane, vEP), four domains, component/data-flow/mode-matrix diagrams, discovery/truth/release/completion/submit reality, GR chain vocabulary, arbitrary-CUDA program, challenges, iteration plan, portability paths, CUDA/architecture mapping, future gaps, 3D interaction, framing. Tree `66388560`. |
| 2026-10-08 | Review pass: status hash refreshed to `master @ 1f571dda`; drifted line anchors updated for the post-`66388560` code (`svc_thread`, `svc_gr_request`, `svc_pending_add`, H3 guard, readiness gate); GR completion §3.4/§6.1 reconciled with FORENSICS-grv5 (wake-up fires; the counter publication is the leading reading); discovery cadence wording corrected (2-second paced passes, not "milliseconds"); mode-matrix source note corrected. |
+| 2026-10-18 | m91-class I-fetch section: FAULT-CAP first-fault capture (`vgsp-m91: FAULT-CAP`, 20/cycle) and the default-on forwarded-QMD instruction-cache invalidate (`gsp_phys_gr_icache_inv`) with the retry-state analysis (identical content, fresh ring entry + lane-enable replay; stale engine-side fetch state at reused shadow VAs). |
diff --git a/docs/04-implementation/03-execution-frontier.md b/docs/04-implementation/03-execution-frontier.md
index f5546ca..a8e03ee 100644
--- a/docs/04-implementation/03-execution-frontier.md
+++ b/docs/04-implementation/03-execution-frontier.md
@@ -1,11 +1,17 @@
# The Execution Frontier: The GR Compute-Launch Chain
-> **Status: 2026-10-06.** This page describes the current leading edge of
+> **Status: 2026-10-20.** This page describes the current state of
> guest-facing execution: the chain that must fire for a stock guest CUDA
-> kernel to run on the physical GPU, what we have proven about it, where it
-> currently stops, how we know, and the open lines of investigation and
-> theories. It is evidence-led: every statement carries either a live
-> observation, a decompilation finding, or a clearly labelled theory.
+> kernel to run on the physical GPU, what we have proven about it, how we
+> know, and where the leading edge is now. The chain fires end to end:
+> verified guest CUDA clients (vector-add, matrix-multiply and a transformer
+> kernel) execute on the physical GPU, their results verify, and the
+> engine-authored completions relay back to the guest. The leading edge has
+> moved from "can a forwarded launch execute at all?" to breadth and
+> hardening — the large-buffer staged-transfer class, reproducibility across
+> boots, and per-guest execution isolation. It is evidence-led: every
+> statement carries either a live observation, a decompilation finding, or a
+> clearly labelled theory.
>
> Evidence sources for this page: supervised physical cycles (module-owned
> GSP-RM on the reference TU104), offline firmware emulation/probing, the host
@@ -40,40 +46,59 @@ flowchart TB
J["Host polls release, relays to guest;<br/>CUDA wakes"]
```
-| # | Stage | State (2026-10-06) |
+| # | Stage | State (2026-10-20) |
|---|---|---|
| 1 | Guest builds launch + method stream | ✅ observed (stock driver, unmodified) |
| 2 | GPFIFO entry + doorbell | ✅ observed |
| 3 | Host decode + forward stream + QMD prewrite + ring/USERD | ✅ observed (forwarding path) |
| 4 | Front-end fetches the ring entry | ✅ observed (ring cursor advances) |
-| 5 | Front-end executes methods | ✅ observed on the ladder channel (object bind, cache ops, report semaphore) |
-| 6 | **Inline-QMD install** (descriptor materialised into the QMD buffer) | ✗ **not observed** |
-| 7 | **Engine reads the QMD** | ✗ **not observed** |
-| 8 | Context switch loads the channel context | ✅ context validity at dispatch is ruled in (instance flag set) — see §4 |
-| 9 | GR dispatches the grid to the SMs | ✗ never observed on this stack |
-| 10 | Engine authors release + channel semaphore | ✗ absent for forwarded launches |
-| 11 | Host relays completion; guest wakes | ✗ guest waits and times out |
-
-**One-line summary:** the guest, the emulation, the forwarding path and the
-front-end are live; the **launch-descriptor install path** (stages 6–7) is the
-leading edge, and with it stages 9–11.
+| 5 | Front-end executes methods | ✅ observed (object bind, cache ops, report semaphore) |
+| 6 | Inline-QMD install (descriptor materialised into the QMD buffer) | ✅ observed |
+| 7 | Engine reads the QMD | ✅ observed |
+| 8 | Context switch loads the channel context | ✅ context validity at dispatch is ruled in — see §4 |
+| 9 | GR dispatches the grid to the SMs | ✅ observed (the guest's results verify) |
+| 10 | Engine authors release + channel semaphore | ✅ observed — engine-authored releases relay to the guest |
+| 11 | Host relays completion; guest wakes | ✅ observed — the guest's synchronisation waits resolve |
+
+**One-line summary:** the whole chain is live — verified guest CUDA clients
+execute on the physical GPU and wake up correctly. The leading edge is no
+longer the chain itself but its breadth and hardening: the large-buffer
+staged-transfer class, reproducibility across boots, and per-guest execution
+isolation.
---
## 2. Where the leading edge is
-A stock guest program now reaches its kernel launch and submits it through the
-forwarding path. On the wire we observe the ring entry consumed and the
-front-end methods run — but the launch descriptor is never materialised, the
-engine never reads a QMD, no engine-authored completion appears, and the guest
-blocks in its synchronisation wait.
-
-This is **not** a context-validity wall (that was the previous frontier and is
-now ruled out at dispatch, §4), and it is **not** a window-mapping wall for the
-descriptor destination (that install is accepted). The wall is the **form of
-the launch method stream**: what the front-end requires in order to install the
-inline descriptor, and what the engine requires in order to read and dispatch
-it.
+A stock, unmodified guest CUDA program now executes its kernels on the
+physical GPU and verifies its results. On fresh boots the vector-add client
+(`victory_mail`) prints `VADD n=32768 bad=0` + `STATUS OK`, the
+matrix-multiply client (`victory_matmul`) prints `MATMUL n=256 bad=0` +
+`STATUS OK`, and a transformer kernel prints `dh=256 seq=64 bad=0`,
+`FFN4 layers=4 bad=0` + `STATUS OK`. The executing engine is the physical GPU
+under the module's own real GSP-RM session; the host CUDA bridge is not part
+of this path. The engine authors the completions, the host relays them to the
+guest, and the guest's synchronisation waits resolve.
+
+The leading edge has therefore moved from the launch chain itself — it fires —
+to breadth and hardening around it:
+
+* **The large-buffer staged-transfer class.** Multi-megabyte staged transfers
+ between guest memory and device memory are under repair: some large
+ multi-chunk clients still mis-verify.
+* **Reproducibility across boots.** A boot-poisoning defect was root-caused
+ and fixed: under the driver's status-queue mode the emulation read the wrong
+ queue cursor, so under load the reply ring could desynchronise, the first
+ CUDA client would fail, and the rest of that boot was poisoned (the
+ user-visible `STATUS FAIL`-then-freeze report). Fixed; re-measurement
+ across fresh boots is in progress.
+* **Per-guest execution isolation.** Multiple mediated devices with different
+ emulated VRAM sizes and multiple guests already coexist at the device level;
+ per-guest execution isolation for multiple simultaneous CUDA guests is in
+ progress.
+
+No breadth beyond these is claimed: OpenGL/Vulkan, Windows guests, migration
+and performance numbers remain planned.
---
@@ -81,17 +106,17 @@ it.
| Observable | What it proves | What it does **not** prove |
|---|---|---|
-| Ring entry consumed; ring cursor advances | The front-end is alive and fetching | Nothing about the engine |
-| Method execution on the ladder channel (report semaphore, channel semaphore) | The front-end can execute the method vocabulary | **Not** engine execution — these signals can be front-end-authored |
-| Descriptor buffer prewritten by the host with a detectable sentinel | Whether the descriptor was materialised/modified after submission | A read does not modify memory; the sentinel detects installs, not reads |
-| Install sentinel unchanged after submit | The inline-install method did not run | It does not say why (mapping, required control method, or ordering) |
-| Engine-authored release word absent; completion poll times out | The engine did not complete the launch | It does not say whether the engine started |
-| No GR fault recorded | The engine did not raise an error | Silence is not health: a skipped or never-reached launch is silent by construction |
-| Channel instance context flag set | The channel is context-valid at dispatch | Nothing about the descriptor path |
-
-**Consequence:** progress on this frontier must be measured by an
-**engine-authored** signal (the release word advancing) — not by front-end
-signals, which we have already learned to distinguish.
+| Client verification output (`bad=0` + `STATUS OK`) | The guest executed its kernel, read its results back and verified them | by itself, which engine produced the results |
+| Engine-authored release word advancing, relayed to the guest | The GR engine executed the launch and the completion relay path works | nothing about other launches |
+| The guest's synchronisation wait resolving | Completion reached the guest through the relay | nothing about other launches |
+| Ring entry consumed; ring cursor advances | The front-end is alive and fetching | **not** engine execution — these signals can be front-end-authored |
+| Reproduction across fresh boots | The path is not a lucky boot | not yet an "every boot" claim while re-measurement is in progress |
+| No GR fault recorded | The engine did not raise this error | silence is not health: a skipped or never-reached launch is silent by construction |
+
+**Consequence:** acceptance is now measured by verified guest results plus the
+engine-authored release; front-end signals remain necessary but not
+sufficient. A green run counts as progress only when the same acceptance
+reproduces across fresh boots.
---
@@ -111,90 +136,94 @@ signals, which we have already learned to distinguish.
* **Parenting/TSG for the fetch** — both parenting variants fetch identically.
* **The copy-engine path** — CE-class work, its releases and its fault relay
are live (default off); the GR path is a separate problem.
+* **The descriptor-gate line of investigation (the old T1–T4 theories).** The
+ inline-descriptor install, the engine's QMD read, the grid dispatch, the
+ engine-authored release and the guest wake-up are all now observed; the
+ gate they questioned is closed by observation (§1, §7).
+* **The old boot-poisoning defect.** Root-caused to the status-queue
+ consumption cursor and fixed; it is no longer the explanation for a failing
+ first client (§5, issue B).
---
## 5. The clear issues (ranked)
-**Issue A — the forwarded stream carries a verbatim guest prelude that cannot
-complete.**
-The forwarded stream includes a verbatim copy of the guest's prelude whose
-inline-data writes target addresses that are not present in the engine's view
-of the channel's page tables, and the referenced data is not relocated. The
-shorter, proven stream shape fires its tail (channel semaphore) but still does
-not schedule the descriptor.
-*How we know:* the prelude stalls in the forwarded form; the shorter shape's
-tail fires; the descriptor-install sentinel never changes in either case.
-*If fixed:* the install path is reached and we can observe stage 6 directly.
-
-**Issue B — the descriptor-virtualization control method is absent.**
-The method that arms the inline-descriptor install path (the QMD
-virtualization control) is not present in the forwarded stream.
-*How we know:* the method does not appear in the submitted stream, and the
-install sentinel never changes.
-*If fixed:* the front-end should materialise the descriptor — falsifiable in
-one cycle by watching the sentinel and the engine-read signal.
-
-**Issue C — diagnostic hygiene.**
-The descriptor sentinel placed at word 0 overlaps real descriptor fields; it
-must be moved to an unused word so diagnostics do not perturb what they
-measure.
+**Issue A — the large-buffer staged-transfer class.**
+Multi-megabyte staged transfers between guest memory and device memory are
+under repair: some large multi-chunk clients still mis-verify.
+*How we know:* the small and medium clients (vector-add, matrix-multiply, the
+transformer kernel) verify green while large multi-chunk transfers mis-compare
+or stall in the same configurations.
+*If fixed:* the client battery should accept the large-buffer class with the
+same verification discipline as the green clients.
+
+**Issue B — reproducibility across boots (re-measurement).**
+A boot-poisoning defect was root-caused and fixed: under the driver's
+status-queue mode the emulation read the wrong queue cursor, so under load the
+reply ring could desynchronise, the first CUDA client would fail, and the rest
+of that boot was poisoned (the user-visible `STATUS FAIL`-then-freeze report).
+*How we know:* the failure was reproduced, the cursor rule was corrected, and
+per-client unregister and session flush work again.
+*If fixed:* green on every fresh boot — re-measurement is in progress, and no
+"every boot" claim is made until it completes.
+
+**Issue C — per-guest execution isolation.**
+Multiple mediated devices with different emulated VRAM sizes and multiple
+guests already coexist at the device level; per-guest execution isolation for
+multiple simultaneous CUDA guests is in progress.
+*How we know:* the device-level coexistence is live; execution isolation is
+not complete.
+*If fixed:* multiple CUDA guests execute simultaneously without interference.
---
## 6. Lines of investigation (live)
-1. **Restore the proven launch shape** as the baseline (the shorter stream that
- fires its tail) and **re-add the virtualization control method** — one
- variable at a time, with the install sentinel and the engine-read signal as
- the readout.
-2. **Relocate or drop the verbatim prelude** so the inline-data writes target
- addresses present in the engine's view, or replace it with the equivalent
- host-side staging the proven shape already uses.
-3. **Move the sentinel** and re-run to confirm the readout is clean.
-4. **Diff against a known-good reference.** Two ways to obtain one: (a) make
- our own ladder dispatch a descriptor built from the same source material
- (in-house reference), or (b) capture a working launch of the stock stack on
- the same GPU (real method stream + descriptor + engine-visible channel
- state) and compare field by field. Either reference converts the remaining
- work from inference to differential.
-5. **Offline probing** of any rule the front-end or engine is believed to
- require, using the firmware emulator/probe tooling before spending a
- supervised cycle.
+1. **Repair the large-buffer staged-transfer class** — the multi-megabyte
+ guest↔device transfers that still mis-verify. The three green clients stay
+ the regression battery and must remain green throughout.
+2. **Complete the fresh-boot re-measurement** — demonstrate the fixed boot
+ path across fresh boots before claiming boot-to-boot reproducibility, and
+ keep the re-measured battery as the regression gate.
+3. **Per-guest execution isolation** — build from the device-level
+ coexistence of multiple mediated devices and guests to per-guest execution
+ isolation.
+4. **Hold the acceptance battery** — vector-add, matrix-multiply and the
+ transformer kernel run as a battery on every re-measurement; each entry is
+ accepted only on its own verification output (`bad=0` + `STATUS OK`).
---
-## 7. Theories (labelled; not yet proven)
-
-* **T1 — the virtualization control is the gate.** The front-end will not
- materialise an inline descriptor until the virtualization-control method
- enables the install path; its absence alone explains the inert descriptor.
-* **T2 — materialise-then-read.** The engine only schedules a descriptor that
- exists in its own view; an install targeting unmapped destinations aborts
- silently rather than faulting.
-* **T3 — the verbatim prelude poisons the launch.** Even where the tail shape
- works, the prelude's refused writes leave the launch path in a state that
- prevents the descriptor from being scheduled.
-* **T4 — relocation is the missing transform.** The guest's inline descriptor
- is valid, but it is built for the guest's view of memory; the forwarding path
- must relocate referenced data (and any embedded pointers) into the channel's
- view before install.
-
-Each theory has a one-cycle falsifier: the install sentinel, the engine-read
-signal, and the engine-authored release, in that order.
+## 7. Theories, resolved
+
+The descriptor-gate theories (T1–T4) are resolved and are not kept as open:
+the inline-descriptor install, the engine's QMD read, the grid dispatch, the
+engine-authored release and the guest wake-up are all directly observed (§1),
+so the launch chain they questioned is now proven end to end.
+
+The remaining leading-edge work is engineering rather than theory: the
+large-buffer transfer class, boot-to-boot reproducibility, and per-guest
+execution isolation. Each has an acceptance signal named in §5 before the
+work.
---
## 8. Claim discipline
-* **Engine-authored release is the only proof of execution.** Front-end
- signals (ring cursor, method execution, semaphores that the front-end can
- author) are necessary but not sufficient.
+* **Engine-authored release is the acceptance signal for execution** — now
+ observed end to end for the verified clients; the guest's own verification
+ (`bad=0` + `STATUS OK`) is the guest-side acceptance signal.
+* **Front-end signals are necessary but not sufficient.** Ring cursor and
+ front-end method execution can be front-end-authored; they never substitute
+ for the engine-authored release.
* **Silence is not health.** No fault recorded does not mean no fault
occurred; the current GR path runs without an error-reporting object, which
is itself a known observability gap.
* **Emulator verdicts are L0/L1.** They bound behaviour, never timing or
device environment.
+* **A green run counts only when it reproduces across fresh boots.** The
+ boot-poisoning defect is the worked example: one green boot is not
+ reproducibility.
* **A wall is closed only by an acceptance signal**, and the acceptance signal
for this frontier is named before the experiment.
@@ -218,3 +247,4 @@ signal, and the engine-authored release, in that order.
| Date | Change |
|---|---|
| 2026-10-06 | Created: the GR compute-launch chain, the leading edge (inline-descriptor install), evidence limits, exonerated walls, ranked issues, live investigation lines and theories. |
+| 2026-10-20 | Chain complete: verified guest CUDA clients execute on the physical GPU (vector-add, matrix-multiply, transformer; engine-authored completions relay and guest waits resolve). Leading edge moved to the large-buffer staged-transfer class, fresh-boot reproducibility (boot-poisoning defect fixed) and per-guest execution isolation; the descriptor-gate theories are resolved. |
diff --git a/docs/05-operations/05-gpubreach-audit.md b/docs/05-operations/05-gpubreach-audit.md
new file mode 100644
index 0000000..3c60385
--- /dev/null
+++ b/docs/05-operations/05-gpubreach-audit.md
@@ -0,0 +1,95 @@
+# GPUBreach-class audit and threat model (vGPU host module)
+
+Status: audit performed 2026-10-21 against the host module at `229bec2c`
+(`vgsp.ko`, `kernel/`), with the write-path guards landed in the same window
+(`7d4919dd`, `7a1f4805`). Scope: every host-side write whose target address
+derives from guest-supplied data. Companion reading:
+[`01-performance-correctness-security.md`](01-performance-correctness-security.md).
+
+## 1. What the GPUBreach threat is
+
+GPUBreach (IEEE S&P 2026, U. Toronto) is a GPU Rowhammer chain: the attacker
+flips bits in the physical GPU's page tables (PTEs), which turns the GPU's
+MMU into an arbitrary *device-memory* read/write primitive; the paper then
+escalates to CPU root by exploiting memory-safety bugs in the *driver* code
+that consumes those corrupted translations. It works with the IOMMU on,
+because the corruption happens inside the GPU, after translation.
+
+The relevant question for this project is therefore not "can a guest read
+device memory" (device memory is the guest's own resource here) but:
+
+1. can guest-influenced data reach a *host* write without validation, and
+2. what bounds a write that lands on a *wrong* target.
+
+## 2. Structural mitigations (why the host is not on that path)
+
+* **Guest GPU page tables are host-emulated.** The guest's GMMU page tables
+ live in the emulated vidmem shadow (BAR1 backing) or in guest RAM; the
+ host resolves guest GPU VAs through its own walk of the guest's published
+ page directories (`SET_PAGE_DIRECTORY`) or through content-verified pins.
+ There is no physical GPU MMU walk of guest-authored tables.
+* **The physical GPU's PTEs are host-owned.** Backend jobs are submitted by
+ the host module on host-authored PTE lines; the guest never supplies a
+ descriptor that the real hardware MMU consumes for host memory.
+* **Every write is aperture-bounded.** The one write primitive,
+ `svc_write_ap()`, sends SYS writes through `vfio_dma_rw()` on the mdev's
+ own VFIO container - the IOMMU/vfio layer only maps the guest's RAM - and
+ FB writes are bounds-checked against the emulated shadow size. A
+ guest-influenced target can therefore only ever land inside the guest's
+ own RAM or the vidmem shadow; a wrong target is a correctness bug, not a
+ host-memory write.
+* **No guest DMA to host buffers.** The guest's drivers talk to the
+ emulated device; they cannot address host kernel buffers. IOMMU is on
+ (vfio), and the mdev exposes only guest RAM plus the emulated windows.
+* **Guest descriptors are not trusted by construction.** Every path that
+ turns a guest VA into a host PA goes through the resolution chain
+ (guest-verified injected pins -> write/read landing ledger -> published
+ page-directory walk -> targeted record scan), and the chain's outputs are
+ re-validated per page (see §3).
+
+## 3. Write-path inventory and validation state
+
+| Write path | Target derivation | Validation today |
+|---|---|---|
+| CE fill / memset (`svc_fill`) | guest VA or physical FB/GPA from the push | per-page destination resolution (pin -> ledger -> walk), page-boundary clamped; a page with no resolution truncates loudly and demands the VA through the pin channel (`FILL-TRUNC`, `stalled VA`); zero fills onto page-table-shaped guest pages are refused (`FILL-PT-REFUSED`); below-floor window fills that resolve outside vidmem are refused (`LOWFILL-REFUSED`) |
+| CE copy (`svc_copy`) | dst/src VAs from the push | per-page resolution with pin-agreement (a fresh pin beats a stale ledger entry and repairs it); device-dst copies without a pin-resolved translation are held (`sema_copy_dst_pin_guard`) |
+| GRCE inline writes (`LOAD_INLINE_DATA`) | destination VA from the push | once-only identity gate keyed by (push VA, push content hash, dst, bytes), session-scoped and 8192 slots wide |
+| GR launch operand staging (`svc_gr_data_fill`) | argument pointers scanned from the QMD | per-page pin/ledger resolution; the resolution is stored in the ledger as the landing authority; dropped pages are named (`GRFILL-DROP`) and demanded |
+| Launch copy-out (engine results back to the shadow/guest page) | the launch's own landing records | per-page destination resolution; every write is witnessed (`OUT-PAGE-WIT`/`OUT-PAGE-POST`); staged-identity rule (only the page staged into a slot may receive the slot's content) |
+| Release/counter writes (semaphores, completion counters) | guest semaphore VAs from pushes | pool-range writes go through a pin/record-verified cache (`pool-pa`), re-validated against the current pin before the 4-byte write; exact-slot fallback for the client's tracker; forward-only apply |
+| GPFIFO/USERD writes (put/get bookkeeping) | channel registration data | the channel's own USERD/instance blocks from the alloc params, aperture-tagged |
+| Window/PTE installs (forward PDE install) | the launch's windows | installs are refused loudly when the backend rejects the PDE; the regular path leaves guest VAs in place (no host-side table mutation) |
+
+## 4. Residual risks and findings
+
+* **Wrong-but-valid writes remain possible** when every resolver agrees on a
+ stale page (e.g. a guest freed and reallocated a VA). The r391/r398/r401
+ fixes removed the known instances (contiguous fill past the first page,
+ replayed inline writes, capture-window eviction), but the class is bounded,
+ not eliminated: the guards convert silent corruption into loud
+ truncation/refusal where a page is unresolved, and the ledger is
+ session-scoped and pin-repaired where a fresh pin exists.
+* **The boot-corruption lottery** (a zero fill landing on guest page-table
+ pages) is structurally mitigated by the page-table-shape refusal and the
+ per-page clamping; no instance has been observed since r391.
+* **Diagnostics stay on**: `DST-DIVERGE` (copy-dst ledger vs probe),
+ `FILL-TRUNC`, `GRFILL-DROP`, `NOWRITE-*` and the stalled-VA demand rows
+ exist so a future divergence is visible in the boot log rather than
+ silent.
+* **What this audit does not claim**: no isolation or side-channel claim.
+ GPU Rowhammer itself is a *physical* fault-injection threat that this
+ software emulation cannot mitigate; the mitigation argument here is only
+ that the host write paths cannot be steered outside the guest's own
+ memory, and that wrong targets are bounded by IOMMU-mapped guest RAM or
+ the emulated shadow.
+
+## 5. Recommended follow-ups
+
+1. Keep the copy-destination ledger under the `DST-DIVERGE` witness and
+ promote it to a bounded hold only if a failing run names it.
+2. Extend the pin-demand channel to client device VAs (today the in-guest
+ scanner resolves only its own push/table windows), so an unresolved
+ operand page becomes a wait on real data instead of a truncated fill.
+3. Re-run this audit when the write-path guards change (any new
+ `svc_write_ap()` caller with a guest-derived target is in scope by
+ default).
diff --git a/docs/README.md b/docs/README.md
index 13f0ae5..27e04e9 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -183,6 +183,7 @@ Read in order if you are new. Jump directly if you know what you need.
| 23 | [GSP in a RISC-V VM (risc-v-gsp-rm-debugger, experimental)](05-operations/03-risc-v-gsp-rm-vm.md) — implemented, documented and tested ([audit](../debug/risc-v-gsp-rm-vm/docs/27-TOOL-AUDIT.md); [value demos](../debug/risc-v-gsp-rm-vm/docs/25-VALUE-VALIDATION.md)) | developers, analysts | 15 min |
| 24 | [risc-v-gsp-rm-debugger user guide (experimental)](05-operations/04-risc-v-gsp-rm-debugger-user-guide.md) — implemented, documented and tested ([audit](../debug/risc-v-gsp-rm-vm/docs/27-TOOL-AUDIT.md); [value demos](../debug/risc-v-gsp-rm-vm/docs/25-VALUE-VALIDATION.md)) | developers, analysts | 15 min |
| 25 | [Defensive Operations Runbook](RUNBOOK.md) | maintainers, operators | 20 min |
+| 25a | [GPUBreach-class audit and threat model](05-operations/05-gpubreach-audit.md) | developers, auditors | 15 min |
| 26 | [Engineering operations](eng-ops.md) — tool admission and workflows (Part I) plus live physical-backend operations (Part II) | maintainers, developers | 20 min |
| 27 | [Hardware and software matrix](06-portability/hardware-software-matrix.md) | developers, integrators | 20 min |
| 28 | [Developer onboarding and standards](07-developers/onboarding-and-standards.md) | contributors | 20 min |