diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-08 18:24:20 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-08 18:24:20 -0400 |
| commit | c31485b90d903bf8d3b20805b6aee2591911ad63 (patch) | |
| tree | 1066e317b914bee5b0ba17b3fbee6093bc556465 | |
| parent | 05dd3b80fac380a786ce01da1820e2b63baffe4c (diff) | |
| download | open-iov-c31485b90d903bf8d3b20805b6aee2591911ad63.tar.gz open-iov-c31485b90d903bf8d3b20805b6aee2591911ad63.zip | |
vgsp-sema: reject identity pool-semaphore translations (the wedge class)
Wedges (WEDGE-COMPARE-r7-vs-r10.md) wrote the pool completion to pa==va
(0x121000000, src=trusted) instead of the guest's real page (greens use
src=pin); the read-back proof cannot catch identity because the SYS
aperture reads/writes accept any address, so it cached and the guest's
first tracker wait never woke (kernel logging stops at client init, guest
wedges). Never accept or cache a pool VA->PA resolved to an identity page:
guard lookup/cache/store and every resolver source; a rejected identity
misses, so the release queues in svc_pending[] until the real pin lands.
| -rw-r--r-- | kernel/gsp_sema_service.c | 54 |
1 files changed, 51 insertions, 3 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index 7837d32..611bb35 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -2184,6 +2184,7 @@ static u32 svc_pool_pa_logged_n; static u64 svc_pool_pa_miss_log; static u32 svc_pool_pa_hit_log; static u32 svc_pool_wr_log; +static u32 svc_pool_ident_rej_dbg; /* First-use-per-page proof line; bounded for the boot. */ static void svc_pool_pa_note(u32 h, u64 va, u64 pa, const char *src) @@ -2228,6 +2229,24 @@ static void svc_pool_wr_note(u32 h, u64 va, u64 pa, const char *src, } } +/* + * Identity translations for pool semaphore VAs are never legitimate: the + * pool is a client GPU-VA range whose backing pages live at distinct guest + * PAs. A degenerate source (stale/misparsed rm_mem record, walk over a + * recycled directory, heuristic map entry) can yield PA == VA; the + * read-back proof does NOT catch it because the SYS aperture happily + * reads/writes at that address, and once cached it poisons every pool + * release: the completion write lands where the guest never polls, the + * client's first tracker wait never wakes, and the guest wedges + * (WEDGE-COMPARE-r7-vs-r10.md: wedges wrote pa=0x121000000 src=trusted, + * greens pa=<guest RAM> src=pin). Never accept or cache one. + */ +static inline bool svc_pool_pa_identity(u64 va, u64 pa) +{ + return (pa & ~(u64)(SEMA_PAGE_SIZE - 1)) == + (va & ~(u64)(SEMA_PAGE_SIZE - 1)); +} + static int svc_pool_pa_lookup(u64 va, u64 *pa, int *ap) { u64 page = va & ~(u64)(SEMA_PAGE_SIZE - 1); @@ -2235,6 +2254,12 @@ static int svc_pool_pa_lookup(u64 va, u64 *pa, int *ap) for (i = 0; i < svc_pool_pa_n; i++) { if (svc_pool_pa[i].va_page == page) { + /* A poisoned (identity) entry must not serve. */ + if (svc_pool_pa_identity(page, + svc_pool_pa[i].pa_page)) { + svc_pool_pa[i].va_page = ~0ULL; + return -ENOENT; + } *pa = svc_pool_pa[i].pa_page | (va & (SEMA_PAGE_SIZE - 1)); *ap = svc_pool_pa[i].ap; @@ -2249,6 +2274,9 @@ static void svc_pool_pa_store(u64 va, u64 pa, int ap) u64 page = va & ~(u64)(SEMA_PAGE_SIZE - 1); u32 i; + /* Never cache an identity translation (see svc_pool_pa_identity). */ + if (svc_pool_pa_identity(page, pa)) + return; for (i = 0; i < svc_pool_pa_n; i++) { if (svc_pool_pa[i].va_page == page) { svc_pool_pa[i].pa_page = @@ -2352,7 +2380,8 @@ static int svc_pool_pa_resolve(struct nv_vgpu_device *vgpu, u32 h, u64 va, u64 exact = (wpa & ~(u64)(SEMA_PAGE_SIZE - 1)) | (va & (SEMA_PAGE_SIZE - 1)); - if (!svc_read_ap(vgpu, exact, SVC_APERTURE_SYS, &probe, + if (!svc_pool_pa_identity(va, exact) && + !svc_read_ap(vgpu, exact, SVC_APERTURE_SYS, &probe, sizeof(probe))) { svc_pool_pa_store(va, exact, SVC_APERTURE_SYS); svc_tr_clear_failure(page); @@ -2392,7 +2421,8 @@ static int svc_pool_pa_resolve(struct nv_vgpu_device *vgpu, u32 h, u64 va, u64 exact = (wpa & ~(u64)(SEMA_PAGE_SIZE - 1)) | (va & (SEMA_PAGE_SIZE - 1)); - if (!svc_read_ap(vgpu, exact, svc_ap_of(wap), &probe, + if (!svc_pool_pa_identity(va, exact) && + !svc_read_ap(vgpu, exact, svc_ap_of(wap), &probe, sizeof(probe))) { svc_pool_pa_store(va, exact, wap); svc_tr_clear_failure(page); @@ -2406,7 +2436,8 @@ static int svc_pool_pa_resolve(struct nv_vgpu_device *vgpu, u32 h, u64 va, } if (!svc_targeted_resolve(vgpu, va, &pa)) { /* Read back before trusting: an unreadable page is not kept. */ - if (!svc_read_ap(vgpu, pa, SVC_APERTURE_SYS, &probe, + if (!svc_pool_pa_identity(va, pa) && + !svc_read_ap(vgpu, pa, SVC_APERTURE_SYS, &probe, sizeof(probe))) { svc_pool_pa_store(va, pa, SVC_APERTURE_SYS); svc_tr_clear_failure(page); @@ -2430,6 +2461,23 @@ static int svc_pool_pa_resolve(struct nv_vgpu_device *vgpu, u32 h, u64 va, * RAM. Proof-gated like every other source. */ if (!svc_lookup_trusted(vgpu, va, &pa, ap)) { + /* + * The last resort can return a degenerate identity mapping + * (PA == VA) that the read-back proof cannot catch; accepting + * it caches a poisoned translation and the completion write + * lands where the guest never polls (the wedge class: + * WEDGE-COMPARE-r7-vs-r10.md). Reject and miss instead so the + * release queues in svc_pending[] and retries until the real + * (pin/walk) translation lands. + */ + if (svc_pool_pa_identity(va, pa)) { + if (svc_pool_ident_rej_dbg < 16) { + svc_pool_ident_rej_dbg++; + pr_info("vgsp-sema: pool-pa identity rejected ch=0x%x va=0x%llx pa=0x%llx (never legit; release queues for the real pin)\n", + h, va, pa); + } + return -ENOENT; + } if (!svc_read_ap(vgpu, pa, svc_ap_of(*ap), &probe, sizeof(probe))) { svc_pool_pa_store(va, pa, *ap); |
