summaryrefslogtreecommitdiffstats
path: root/kernel/gsp_sema_service.c
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-28 16:07:49 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-28 16:07:49 -0400
commit407f3edf2a9afa9032991679a35655d1e4cb0801 (patch)
tree8b0d82a52a3e181aa3dccb61f9d866947c8d41dd /kernel/gsp_sema_service.c
parentbea420425bd67fbd2b7455bbb0467c979df0debc (diff)
downloadopen-iov-407f3edf2a9afa9032991679a35655d1e4cb0801.tar.gz
open-iov-407f3edf2a9afa9032991679a35655d1e4cb0801.zip
sema: refuse work from buffers that do not start like a push
The discovered VA->PA map is content-scanned and can hand a ring entry's push VA a false translation; observed: GSP firmware pages read as a 12700-byte method stream whose fills would have been executed into vidmem. Work now requires the buffer to start with a plausible first method (INC_METHOD, address 0, sane count that fits the length); releases still apply from such buffers so a shifted stream is not lost. New 'denied' counter in the sweep stats.
Diffstat (limited to 'kernel/gsp_sema_service.c')
-rw-r--r--kernel/gsp_sema_service.c28
1 files changed, 26 insertions, 2 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index 5505c1e..ba3690d 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -314,6 +314,7 @@ struct sema_svc {
u64 ring_ops; /* op runs from ring-decoded pushes */
u64 lda_log; /* LAUNCH_DMA inventory budget */
u64 entry_log; /* per-push summary budget */
+ u64 ops_denied; /* work refused: implausible stream */
u64 releases_seen;
u64 releases_applied;
u64 releases_missed; /* no VA->PA entry */
@@ -1460,10 +1461,10 @@ static void svc_rebuild(struct nv_vgpu_device *vgpu)
svc.releases_stale, svc.releases_jumped,
svc.walks_ok, svc.walks_ok + svc.walks_miss, svc.lat_hits,
svc_scan_cursor);
- pr_info("sema-svc: pages=%llu pushes=%llu pending=%llu nomap=%llu memset=%llu/%llu/%llu ringops=%llu\n",
+ pr_info("sema-svc: pages=%llu pushes=%llu pending=%llu nomap=%llu memset=%llu/%llu/%llu ringops=%llu denied=%llu\n",
svc.pages_decoded, svc.pushes_seen, svc.pushes_pending,
svc.pushes_nomap, svc.memsets_done, svc.memsets_nomap,
- svc.memsets_fail, svc.ring_ops);
+ svc.memsets_fail, svc.ring_ops, svc.ops_denied);
pr_info("sema-svc: gpfifo done=%llu nomap=%llu vidmem_writes=%llu ring_auto=%u pb=%u/%u\n",
svc.gpfifo_entries_done, svc.gpfifo_nomap,
svc.vidmem_writes, svc_ring_n, svc_pb_n, svc_pb_passes);
@@ -1732,6 +1733,29 @@ static void svc_decode_stream(struct nv_vgpu_device *vgpu, const u8 *buf,
u32 ops_runs = 0;
u32 off = 0;
+ /*
+ * A false VA->PA pair (the discovered map is content-scanned) can
+ * point a ring entry's push VA at an unrelated page - observed:
+ * GSP firmware pages decoded as a 12700-byte "method stream" whose
+ * fills would have been executed into vidmem. Work may only run
+ * when the buffer starts like a push (first method: op=INC_METHOD,
+ * addr 0, sane count). Releases from such a page still apply, so a
+ * genuinely shifted stream is not lost.
+ */
+ if (allow_ops) {
+ u32 hdr = 0, op = 0, count = 0;
+
+ if (len >= 8)
+ memcpy(&hdr, buf, 4);
+ op = (hdr >> 29) & 0x7;
+ count = (hdr >> 16) & 0xfff;
+ if (op != 1 || !count || count > 64 ||
+ 4 + count * 4 > len || ((hdr & 0x1fff) << 2) != 0) {
+ svc.ops_denied++;
+ allow_ops = false;
+ }
+ }
+
memset(&st, 0, sizeof(st));
svc.pages_decoded++;