summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-08 18:24:20 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-08 18:24:20 -0400
commitc31485b90d903bf8d3b20805b6aee2591911ad63 (patch)
tree1066e317b914bee5b0ba17b3fbee6093bc556465 /kernel
parent05dd3b80fac380a786ce01da1820e2b63baffe4c (diff)
downloadopen-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.
Diffstat (limited to 'kernel')
-rw-r--r--kernel/gsp_sema_service.c54
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);