summaryrefslogtreecommitdiffstats
path: root/kernel/gsp_sema_service.c
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-28 09:31:48 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-28 09:31:48 -0400
commitcc1de524d8cea869daacc652108c76f403eb542d (patch)
treef196e2bed47f5bbab369be0e4d38a526382044bc /kernel/gsp_sema_service.c
parentf3c4419a64b69f85b8a7170ff5d15c0bef802a02 (diff)
downloadopen-iov-cc1de524d8cea869daacc652108c76f403eb542d.tar.gz
open-iov-cc1de524d8cea869daacc652108c76f403eb542d.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.c35
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)