From cc1de524d8cea869daacc652108c76f403eb542d Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Mon, 28 Sep 2026 09:31:48 -0400 Subject: sema: never execute work decoded from swept RAM; pin only tracker sems The content sweep reads arbitrary guest RAM and ran the full push decoder on every page, so fabricated method streams in unrelated data executed fills/copies: one boot scanned 65M pages, 'decoded' 124k pushes and ran dozens of memsets from false positives - writes into live guest memory (a guest kernel crashed in nvidia_uvm right after such a boot). - Swept pages now only permit the bounded semaphore-release writes; fills and copies execute only for pushes the pump read through a trusted ring translation. - Pushes read for a compute (GR) channel apply releases only: their method space is not the copy engine's, so aliased encodings must never become memory writes. - sempages.py derives the pages to pin from the client's tracker objects (sem object + sem memory) instead of matching bare sem-object shapes, which pinned 140 pages where the client has ~53 sems; shape scan remains as a fallback when no tracker resolves. --- kernel/gsp_sema_service.c | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) (limited to 'kernel/gsp_sema_service.c') diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index bf6bb01..40926ee 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -1719,7 +1719,7 @@ static void svc_run_ops(struct nv_vgpu_device *vgpu, struct svc_memset_op *ops, * skipped, so their stale memsets never overwrite newer state. */ static void svc_decode_stream(struct nv_vgpu_device *vgpu, const u8 *buf, - u32 len, u64 page_pa) + u32 len, u64 page_pa, bool allow_ops) { struct svc_push_state st; struct svc_memset_op ops[SVC_MAX_MEMSETS]; @@ -1886,7 +1886,8 @@ static void svc_decode_stream(struct nv_vgpu_device *vgpu, const u8 *buf, if (st.rs_payload <= cur) break; svc.pushes_pending++; - svc_run_ops(vgpu, ops, n_ops); + if (allow_ops) + svc_run_ops(vgpu, ops, n_ops); n_ops = 0; if (!svc_sem_write(vgpu, va, st.rs_payload)) @@ -1953,7 +1954,8 @@ static void svc_decode_stream(struct nv_vgpu_device *vgpu, const u8 *buf, u32 dma = v0; u32 transfer = DMA_DATA_TRANSFER_TYPE(dma); - if (transfer != 0 && st.line_length && st.have_out) { + if (allow_ops && transfer != 0 && st.line_length && + st.have_out) { if (DMA_REMAP_ENABLE(dma) && st.have_remap && DMA_DST_MEMORY_LAYOUT_PITCH(dma) && n_ops < SVC_MAX_MEMSETS) { @@ -2014,7 +2016,8 @@ static void svc_decode_stream(struct nv_vgpu_device *vgpu, const u8 *buf, if (pending) { svc.pushes_pending++; - svc_run_ops(vgpu, ops, n_ops); + if (allow_ops) + svc_run_ops(vgpu, ops, n_ops); if (DMA_SEM_REDUCTION_EN(dma) && DMA_SEM_REDUCTION(dma) == SEM_REDUCTION_INC) @@ -2137,8 +2140,15 @@ static u32 svc_apply_page_data(struct nv_vgpu_device *vgpu, const u8 *buf, * Decoded execution: walk the page as a method stream and perform the * pushed work (PTE memsets, semaphore releases) for pushes that are * still pending. + * + * This page was reached by sweeping raw guest RAM, so the data is + * arbitrary: only the bounded semaphore-release writes below may be + * attempted. Executing fills/copies decoded from a swept page is what + * made the sweep corrupt live guest kernel memory (65M pages scanned, + * fabricated "pushes" executing memsets); work execution is limited + * to pushes the pump read through a trusted ring translation. */ - svc_decode_stream(vgpu, buf, SEMA_PAGE_SIZE, pa_page); + svc_decode_stream(vgpu, buf, SEMA_PAGE_SIZE, pa_page, false); for (off = 0; off + 16 <= SEMA_PAGE_SIZE; off += 4) { u32 hdr, hi, lo, payload; @@ -3185,8 +3195,18 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu) if (pb) { if (!svc_read_va_trusted(vgpu, va, pb, len)) { + /* + * Fills/copies execute only for CE + * channels: the push was written for the + * copy engine, so its method space is + * CE's. Compute pushes (context binds, + * work submissions) carry releases we + * need but their other methods may + * alias CE encodings. + */ svc_decode_stream(vgpu, pb, - len, 0); + len, 0, + ch->engine_type != 0); ch->submissions++; svc.gpfifo_entries_done++; /* Advance expected payload for this channel's tracking semaphore. */ @@ -3205,7 +3225,8 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu) if (!svc_read_va_trusted(vgpu, va, pb, len)) { svc_decode_stream(vgpu, pb, - len, 0); + len, 0, + ch->engine_type != 0); ch->submissions++; svc.gpfifo_entries_done++; if (ch->engine_type && ch->expected_sem_payload) -- cgit v1.2.3