diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-09-28 09:31:48 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-09-28 09:31:48 -0400 |
| commit | 7023742accd4b2bf5dbeaf82907e465c905dbdad (patch) | |
| tree | d4dc733894235a8198bb38909701387a22d86cb2 /kernel/gsp_sema_service.c | |
| parent | ab393b15abac5b6c0790bb4c4fd86bc62c531bf4 (diff) | |
| download | open-iov-7023742accd4b2bf5dbeaf82907e465c905dbdad.tar.gz open-iov-7023742accd4b2bf5dbeaf82907e465c905dbdad.zip | |
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.
Diffstat (limited to 'kernel/gsp_sema_service.c')
| -rw-r--r-- | kernel/gsp_sema_service.c | 35 |
1 files changed, 28 insertions, 7 deletions
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) |
