diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-11-13 11:30:46 -0500 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-11-13 11:30:46 -0500 |
| commit | e1eac3685d6c4208ce7e47bd621e31b76e194811 (patch) | |
| tree | 0ae623659151812ef9fcc55b83267452fbd7ecf9 /kernel | |
| parent | 42a1c00e335f1f9dc41e82f512f268c97363b186 (diff) | |
| download | open-iov-e1eac3685d6c4208ce7e47bd621e31b76e194811.tar.gz open-iov-e1eac3685d6c4208ce7e47bd621e31b76e194811.zip | |
vgsp: content-gated FB-shadow push rescue for recycled client pushes (eng20, end-to-end validation pending)
The direct-GL client's one-shot 59,556 B setup push (VA 0x4001004) reads
zero at decode time: the live page is recycled before our decode runs, so
no C597 bind attaches for h=0xbeef0100. The rescue recovers the push body
from the FB shadow with strict content gates: reject fill windows (require
>=2 multi-word INC runs + >=8 distinct headers), scan at the push VA's
in-page offset (0x4041004 starts 4 B into a page), and log the channel
handle. eng20 refuted the unregistered-VAS hypothesis: the payload pages
DO walk via VAS 0xbeef0300 (0x4000000->pa=0x342ead000,
0x4001000->pa=0x342eae000, consistent delta). Also found: sema_gl_xlate
(absent from params-r1780; default 0) gates every C597 section-recognition
site - with it on, the lane produced its first C597 captures
(127x c305, 20x c307). Validation cyclic c308 (guest init panic) / c309
(QGA wedge) blocked; no observation yet. Unvalidated end to end.
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/gsp_sema_service.c | 85 |
1 files changed, 76 insertions, 9 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index 9730c58..efb4fc1 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -15838,12 +15838,78 @@ struct svc_fb_push_hit { u64 off; }; -static bool svc_fb_push_rescue(struct nv_vgpu_device *vgpu, u64 va, u32 len, - u8 *out, u64 *off_out) +/* + * CONT.445 follow-up: content-identity gate for the FB push rescue. The raw + * method-stream parse alone cannot admit a rescue window: op 7 (END_PB_ + * SEGMENT) and op 0 (SET/STORE/USE_SUB_DEV_MASK) are 4-byte header-only + * forms, so a fill window (0xff000000 / 0x00000000 / one repeated dword) + * parses cleanly - the V36 rescue false-positived on exactly such an X + * screen fill. A real setup/draw push carries multi-word INC runs and a + * variety of method headers; require both, so a fill can never win. + */ +static bool svc_fb_push_window_ok(const u8 *buf, u32 len) +{ + u32 off = 0, inc = 0; + u32 seen[64]; + u32 nseen = 0; + + if (!svc_method_stream_plausible(buf, len)) + return false; + while (off + 4 <= len) { + u32 hdr, op, cnt; + + memcpy(&hdr, buf + off, 4); + op = (hdr >> 29) & 7u; + cnt = (hdr >> 16) & 0xfffu; + if (op == 1u || op == 3u || op == 5u) { + if (!cnt || cnt > 0xfff || + off + 4 + cnt * 4u > len) + return false; + if (cnt >= 2) + inc++; + off += 4 + cnt * 4u; + } else if (op == 0u || op == 2u || op == 4u || op == 7u) { + if (nseen < ARRAY_SIZE(seen)) { + u32 i; + bool dup = false; + + for (i = 0; i < nseen; i++) + if (seen[i] == hdr) { + dup = true; + break; + } + if (!dup) + seen[nseen++] = hdr; + } + off += 4; + } else { + return false; + } + } + /* + * Measured real pushes: the 59556-byte setup stream and the 6400-byte + * draw streams carry many distinct method headers and several + * multi-word INC runs. 8 distinct headers + 2 INC runs is well below + * any observed real stream and far above any fill window (1 header, + * 0 INC runs). + */ + return inc >= 2 && nseen >= 8; +} + +static bool svc_fb_push_rescue(struct nv_vgpu_device *vgpu, u32 h_object, + u64 va, u32 len, u8 *out, u64 *off_out) { static struct svc_fb_push_hit hits[8]; u8 *base = vgpu->bars[1].vaddr; u64 lim = min_t(u64, svc_fb_shadow_size(vgpu), 0x4000000ULL), off; + /* + * The client writes the pushbuffer at its VA's page offset inside the + * BAR1 window, so a push whose VA is 0x...004 starts 4 bytes into a + * shadow page. Scan at that same in-page offset (the old page-aligned + * scan could never match such a push - e.g. the 59556-byte setup + * push at va 0x4041004). + */ + u32 poff = (u32)(va & (SEMA_PAGE_SIZE - 1)); static u32 log; int i; @@ -15855,19 +15921,19 @@ static bool svc_fb_push_rescue(struct nv_vgpu_device *vgpu, u64 va, u32 len, for (i = 0; i < ARRAY_SIZE(hits); i++) { if (hits[i].n && hits[i].len == len && hits[i].off + len <= lim && - svc_method_stream_plausible(base + hits[i].off, len)) { + svc_fb_push_window_ok(base + hits[i].off, len)) { memcpy(out, base + hits[i].off, len); *off_out = hits[i].off; return true; } } /* Cheap first: the first 64 bytes must parse as a stream prefix. */ - for (off = 0; off + len <= lim; off += SEMA_PAGE_SIZE) { + for (off = poff; off + len <= lim; off += SEMA_PAGE_SIZE) { u32 pre = min_t(u32, len, 64); if (!svc_method_stream_plausible(base + off, pre)) continue; - if (!svc_method_stream_plausible(base + off, len)) + if (!svc_fb_push_window_ok(base + off, len)) continue; memcpy(out, base + off, len); *off_out = off; @@ -15881,15 +15947,15 @@ static bool svc_fb_push_rescue(struct nv_vgpu_device *vgpu, u64 va, u32 len, } if (log < 16) { log++; - pr_info("sema-chan: FB-PUSH rescue h=0x%x va=0x%llx len=%u -> fb 0x%llx (plausible window; pinned for later reads)\n", - va, len, off); + pr_info("sema-chan: FB-PUSH rescue h=0x%x va=0x%llx len=%u -> fb 0x%llx (content-gated window; pinned for later reads)\n", + h_object, va, len, off); } return true; } if (log < 16) { log++; - pr_info("sema-chan: FB-PUSH rescue h=0x%x va=0x%llx len=%u: no plausible window in 0x%llx shadow\n", - va, len, lim); + pr_info("sema-chan: FB-PUSH rescue h=0x%x va=0x%llx len=%u: no content-gated window in 0x%llx shadow\n", + h_object, va, len, lim); } return false; } @@ -46377,6 +46443,7 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu) u64 fbo = 0; if (svc_fb_push_rescue(vgpu, + ch->h_object, va, len, pb, &fbo)) { ok = true; |
