summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-11 15:43:30 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-11 15:43:30 -0400
commit1743c19465f4c024010d37e1fd96e9192cdc4525 (patch)
tree2cdd63b2e6e7b8c194b7e031ceeb8ef0e4d20473 /kernel
parent3d4777f614e31f3c3a4f2ce49b37054a10fd1fd3 (diff)
downloadopen-iov-1743c19465f4c024010d37e1fd96e9192cdc4525.tar.gz
open-iov-1743c19465f4c024010d37e1fd96e9192cdc4525.zip
vgsp-sema: bound emulated-vidmem shadow accesses by the shadow size, not the BAR1 aperture (ISSUE-0002)
The pma scrub in a second/third CUDA session memsets a legitimate vidmem offset at 262M (inside the 512M turing shadow) and the copy path rejected it with -EINVAL because every FB bounds check compared against vgpu->bars[1].size, the physical BAR1 aperture exposed to the guest (256M on TU104). The op was held and retried forever (opdef), the GR scrub waited in scrubCheckAndWaitForSize and the client froze. All bounds checks that guard a dereference of the emulated vidmem shadow at vgpu->bars[1].vaddr now use nv_vgpu_fb_emul_size() via svc_fb_shadow_size(): the copy/fill paths, svc_read_ap/svc_write_ap, svc_ap_ok, the page-directory discovery scans, svc_lookup's vidmem VA translation, the GRCE inline write, svc_fwd_operand_hpa and the debug scans. The 2 MB leaf path of svc_gmmu_walk now validates through svc_ap_ok like every other leaf path so an out-of-shadow translation falls through to the shadow fallback instead of reaching the copy path. The RM page-directory debug peek is bounded the same way. Evidence: r75b live log: sema-bigcopy dst_va=0x76aa08e02200 -> pa=0x10602200 ap=0 prov=3; sema-bigop rc=-22 repeated; guest sysrq stack scrubCheckAndWaitForSize -> pmaAllocatePages.
Diffstat (limited to 'kernel')
-rw-r--r--kernel/gsp_rm_emulation.c9
-rw-r--r--kernel/gsp_sema_service.c89
2 files changed, 66 insertions, 32 deletions
diff --git a/kernel/gsp_rm_emulation.c b/kernel/gsp_rm_emulation.c
index 2ad1ba7..8318f56 100644
--- a/kernel/gsp_rm_emulation.c
+++ b/kernel/gsp_rm_emulation.c
@@ -3381,8 +3381,15 @@ static void gsp_rm_handle_set_page_directory(struct gsp_emu_context *ctx,
if (desc.b_vidmem) {
u8 *base = ctx->vgpu->bars[1].vaddr;
+ /*
+ * Bound against the emulated vidmem shadow
+ * size, not the physical BAR1 aperture: vidmem
+ * offsets above the aperture (e.g. 262M in the
+ * 512M turing type) are legitimate (ISSUE-0002
+ * r75b).
+ */
if (base && phys + sizeof(peek) <=
- ctx->vgpu->bars[1].size) {
+ nv_vgpu_fb_emul_size(ctx->vgpu)) {
memcpy(peek, base + phys, sizeof(peek));
rc = 0;
}
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c
index e972732..ee73027 100644
--- a/kernel/gsp_sema_service.c
+++ b/kernel/gsp_sema_service.c
@@ -53,6 +53,23 @@
#include "gsp_phys_gpu.h"
#include "gsp_cuda_bridge.h"
+/*
+ * ISSUE-0002 (r75b): bounds for accesses through the emulated vidmem shadow
+ * at vgpu->bars[1].vaddr. The shadow is sized by the mdev type
+ * (nv_vgpu_fb_emul_size: 512M/1G/2G) while vgpu->bars[1].size is the physical
+ * BAR1 aperture exposed to the guest (256M on TU104). Every bounds check that
+ * guards a shadow dereference must use the shadow size: a legitimate guest
+ * vidmem offset above the aperture (the pma scrub memset at 262M in a second/
+ * third CUDA session) was rejected as -EINVAL against the aperture bound and
+ * the client froze in scrubCheckAndWaitForSize. Using the shadow size is also
+ * the safe direction in the inverse case: an aperture larger than the shadow
+ * would otherwise let a check pass past the vzalloc'd buffer.
+ */
+static inline u64 svc_fb_shadow_size(const struct nv_vgpu_device *vgpu)
+{
+ return nv_vgpu_fb_emul_size(vgpu);
+}
+
#define SEMA_MAP_MAX 65536
#define SEMA_MAP_SIZE_BITS 16
#define SEMA_PAGE_SIZE 4096
@@ -874,7 +891,7 @@ static int svc_read_ap(struct nv_vgpu_device *vgpu, u64 addr, u32 aperture,
{
if (aperture == SVC_APERTURE_FB) {
u8 *base = vgpu->bars[1].vaddr;
- size_t size = vgpu->bars[1].size;
+ size_t size = svc_fb_shadow_size(vgpu);
if (!base || addr + len > size)
return -EINVAL;
@@ -889,7 +906,7 @@ static int svc_write_ap(struct nv_vgpu_device *vgpu, u64 addr, u32 aperture,
{
if (aperture == SVC_APERTURE_FB) {
u8 *base = vgpu->bars[1].vaddr;
- size_t size = vgpu->bars[1].size;
+ size_t size = svc_fb_shadow_size(vgpu);
if (!base || addr + len > size)
return -EINVAL;
@@ -1273,7 +1290,7 @@ static int svc_pde_aperture(u32 ap)
static bool svc_ap_ok(struct nv_vgpu_device *vgpu, u64 addr, int ap)
{
if (ap == SVC_APERTURE_FB)
- return vgpu->bars[1].vaddr && addr + 8 <= vgpu->bars[1].size;
+ return vgpu->bars[1].vaddr && addr + 8 <= svc_fb_shadow_size(vgpu);
return addr + 8 <= SEMA_PA_MAX;
}
@@ -1397,10 +1414,20 @@ static int svc_gmmu_walk(struct nv_vgpu_device *vgpu, u64 pdb, u32 entries,
if (big & 1) {
bap = svc_pte_aperture((big >> 1) & 0x3);
if (bap >= 0) {
- *addr = svc_pte_addr(big, bap == SVC_APERTURE_FB) +
+ u64 a = svc_pte_addr(big, bap == SVC_APERTURE_FB) +
(va & ((1ULL << 21) - 1));
- *ap = bap;
- return 0;
+
+ /*
+ * ISSUE-0002 (r75b): validate the 2 MB leaf against
+ * the aperture bounds like every other leaf path; an
+ * unchecked out-of-shadow result must fall through to
+ * the shadow fallback, not reach the copy path.
+ */
+ if (svc_ap_ok(vgpu, a, bap)) {
+ *addr = a;
+ *ap = bap;
+ return 0;
+ }
}
}
bap = svc_pde_aperture((big >> 1) & 0x3);
@@ -1603,7 +1630,7 @@ static void svc_pd_discover(struct nv_vgpu_device *vgpu)
* matters is the first one of the page. */
for (pass = 0; pass < 2 && !svc_pd_found; pass++) {
int ap = pass == 0 ? SVC_APERTURE_SYS : SVC_APERTURE_FB;
- u64 limit = pass == 0 ? SEMA_PA_MAX : vgpu->bars[1].size;
+ u64 limit = pass == 0 ? SEMA_PA_MAX : svc_fb_shadow_size(vgpu);
u8 *fb = (u8 *)vgpu->bars[1].vaddr;
u64 gpa;
@@ -2319,7 +2346,7 @@ static int svc_lookup(struct nv_vgpu_device *vgpu, u64 va, u64 *addr, int *ap)
if (!svc_lookup_trusted(vgpu, va, addr, ap))
return 0;
if (va >= SVC_VIDMEM_VA_BASE &&
- va - SVC_VIDMEM_VA_BASE + 4 <= vgpu->bars[1].size) {
+ va - SVC_VIDMEM_VA_BASE + 4 <= svc_fb_shadow_size(vgpu)) {
*addr = va - SVC_VIDMEM_VA_BASE;
*ap = SVC_APERTURE_FB;
svc_lat_store(va, *addr, *ap);
@@ -3679,7 +3706,7 @@ static int svc_fill(struct nv_vgpu_device *vgpu, u64 addr, u64 value, u32 elem,
if (aperture == CE_APERTURE_VID) {
u8 *base = vgpu->bars[1].vaddr;
- size_t size = vgpu->bars[1].size;
+ size_t size = svc_fb_shadow_size(vgpu);
if (!base || addr + total > size)
return -EINVAL;
@@ -4147,7 +4174,7 @@ static int svc_copy(struct nv_vgpu_device *vgpu, u64 dst, u64 src, size_t total,
} else if (src_aperture == CE_APERTURE_VID) {
u8 *base = vgpu->bars[1].vaddr;
- if (base && src + sizeof(sb) <= vgpu->bars[1].size)
+ if (base && src + sizeof(sb) <= svc_fb_shadow_size(vgpu))
memcpy(sb, base + src, sizeof(sb));
} else {
svc_read(vgpu, src, sb, sizeof(sb));
@@ -4157,7 +4184,7 @@ static int svc_copy(struct nv_vgpu_device *vgpu, u64 dst, u64 src, size_t total,
} else if (dst_aperture == CE_APERTURE_VID) {
u8 *base = vgpu->bars[1].vaddr;
- if (base && dst + sizeof(db) <= vgpu->bars[1].size)
+ if (base && dst + sizeof(db) <= svc_fb_shadow_size(vgpu))
memcpy(db, base + dst, sizeof(db));
} else {
svc_read(vgpu, dst, db, sizeof(db));
@@ -4259,7 +4286,7 @@ static int svc_copy(struct nv_vgpu_device *vgpu, u64 dst, u64 src, size_t total,
if (src_aperture == CE_APERTURE_VID) {
u8 *base = vgpu->bars[1].vaddr;
- if (base && src + 8 <= vgpu->bars[1].size)
+ if (base && src + 8 <= svc_fb_shadow_size(vgpu))
memcpy(sb, base + src, 8);
} else {
svc_read(vgpu, src, sb, 8);
@@ -4267,7 +4294,7 @@ static int svc_copy(struct nv_vgpu_device *vgpu, u64 dst, u64 src, size_t total,
if (dst_aperture == CE_APERTURE_VID) {
u8 *base = vgpu->bars[1].vaddr;
- if (base && dst + 8 <= vgpu->bars[1].size)
+ if (base && dst + 8 <= svc_fb_shadow_size(vgpu))
memcpy(db, base + dst, 8);
} else {
svc_read(vgpu, dst, db, 8);
@@ -4319,7 +4346,7 @@ static int svc_copy(struct nv_vgpu_device *vgpu, u64 dst, u64 src, size_t total,
if (src_aperture == CE_APERTURE_VID) {
u8 *base = vgpu->bars[1].vaddr;
- if (base && src + n <= vgpu->bars[1].size)
+ if (base && src + n <= svc_fb_shadow_size(vgpu))
memcpy(peek, base + src, n);
} else {
svc_read(vgpu, src, peek, n);
@@ -4361,7 +4388,7 @@ static int svc_copy(struct nv_vgpu_device *vgpu, u64 dst, u64 src, size_t total,
if (aper == CE_APERTURE_VID) {
u8 *base = vgpu->bars[1].vaddr;
- if (!base || pa + chunk > vgpu->bars[1].size)
+ if (!base || pa + chunk > svc_fb_shadow_size(vgpu))
return -EINVAL;
memcpy(bounce, base + pa, chunk);
} else if (svc_read(vgpu, pa, bounce, chunk)) {
@@ -4369,7 +4396,7 @@ static int svc_copy(struct nv_vgpu_device *vgpu, u64 dst, u64 src, size_t total,
}
} else if (src_aperture == CE_APERTURE_VID) {
u8 *base = vgpu->bars[1].vaddr;
- size_t size = vgpu->bars[1].size;
+ size_t size = svc_fb_shadow_size(vgpu);
if (!base || src + o + chunk > size)
return -EINVAL;
@@ -4391,7 +4418,7 @@ static int svc_copy(struct nv_vgpu_device *vgpu, u64 dst, u64 src, size_t total,
if (aper == CE_APERTURE_VID) {
u8 *base = vgpu->bars[1].vaddr;
- if (!base || pa + chunk > vgpu->bars[1].size)
+ if (!base || pa + chunk > svc_fb_shadow_size(vgpu))
return -EINVAL;
memcpy(base + pa, bounce, chunk);
} else if (svc_write(vgpu, pa, bounce, chunk)) {
@@ -4399,7 +4426,7 @@ static int svc_copy(struct nv_vgpu_device *vgpu, u64 dst, u64 src, size_t total,
}
} else if (dst_aperture == CE_APERTURE_VID) {
u8 *base = vgpu->bars[1].vaddr;
- size_t size = vgpu->bars[1].size;
+ size_t size = svc_fb_shadow_size(vgpu);
if (!base || dst + o + chunk > size)
return -EINVAL;
@@ -4425,7 +4452,7 @@ static int svc_copy(struct nv_vgpu_device *vgpu, u64 dst, u64 src, size_t total,
} else if (dst_aperture == CE_APERTURE_VID) {
u8 *base = vgpu->bars[1].vaddr;
- if (base && dst + sizeof(da) <= vgpu->bars[1].size)
+ if (base && dst + sizeof(da) <= svc_fb_shadow_size(vgpu))
memcpy(da, base + dst, sizeof(da));
} else {
svc_read(vgpu, dst, da, sizeof(da));
@@ -4443,7 +4470,7 @@ static int svc_copy(struct nv_vgpu_device *vgpu, u64 dst, u64 src, size_t total,
if (dst_aperture == CE_APERTURE_VID) {
u8 *base = vgpu->bars[1].vaddr;
- if (base && dst + 8 <= vgpu->bars[1].size)
+ if (base && dst + 8 <= svc_fb_shadow_size(vgpu))
memcpy(da, base + dst, 8);
} else {
svc_read(vgpu, dst, da, 8);
@@ -4460,7 +4487,7 @@ static int svc_copy(struct nv_vgpu_device *vgpu, u64 dst, u64 src, size_t total,
if (dst_aperture == CE_APERTURE_VID) {
u8 *base = vgpu->bars[1].vaddr;
- if (base && dst + n <= vgpu->bars[1].size)
+ if (base && dst + n <= svc_fb_shadow_size(vgpu))
memcpy(da, base + dst, n);
} else {
svc_read(vgpu, dst, da, n);
@@ -4530,7 +4557,7 @@ static int svc_grce_inline_write(struct nv_vgpu_device *vgpu, u64 dst_va,
if (ap == SVC_APERTURE_FB) {
u8 *base = vgpu->bars[1].vaddr;
- if (!base || pa + bytes > vgpu->bars[1].size)
+ if (!base || pa + bytes > svc_fb_shadow_size(vgpu))
return -EINVAL;
memcpy(base + pa, data, bytes);
} else {
@@ -8791,9 +8818,9 @@ static void svc_gr_content_discover(struct nv_vgpu_device *vgpu)
* entry0 must sit at the pinned page's offset 0 for the page-granular
* pin to be correct.
*/
- if (vgpu->bars[1].vaddr && vgpu->bars[1].size) {
+ if (vgpu->bars[1].vaddr && svc_fb_shadow_size(vgpu)) {
const u8 *fb = (const u8 *)vgpu->bars[1].vaddr;
- u64 lim = min_t(u64, vgpu->bars[1].size,
+ u64 lim = min_t(u64, svc_fb_shadow_size(vgpu),
SVC_GR_CONTENT_FB_LIMIT);
u64 off;
@@ -9274,9 +9301,9 @@ static void svc_gr_ring_autopin(struct nv_vgpu_device *vgpu,
* entry0-shaped word (1 MB aligned push VA, sane length). The
* client's ring allocation offset varies per boot; the scan
* names the real location for the fix. */
- if (gsp_sema_dbg && vgpu->bars[1].vaddr && vgpu->bars[1].size &&
+ if (gsp_sema_dbg && vgpu->bars[1].vaddr && svc_fb_shadow_size(vgpu) &&
!svc_fbs_scan_done) {
- u64 lim = min_t(u64, vgpu->bars[1].size, 0x20000000ULL);
+ u64 lim = min_t(u64, svc_fb_shadow_size(vgpu), 0x20000000ULL);
u64 off;
int hits = 0;
@@ -10501,8 +10528,8 @@ static int svc_fwd_operand_hpa(struct nv_vgpu_device *vgpu, u8 kind,
u8 *base = vgpu ? vgpu->bars[1].vaddr : NULL;
struct page *pg;
- if (!base || page > vgpu->bars[1].size ||
- (u64)page_off + bytes > vgpu->bars[1].size - page)
+ if (!base || page > svc_fb_shadow_size(vgpu) ||
+ (u64)page_off + bytes > svc_fb_shadow_size(vgpu) - page)
return -ERANGE;
pg = vmalloc_to_page(base + page);
if (!pg)
@@ -12858,7 +12885,7 @@ static void svc_userd_sweep(struct nv_vgpu_device *vgpu, struct svc_channel *ch,
pr_info("sema-userd-sweep: h=0x%x e0=0x%x e1=0x%x\n",
ch->h_object, e0, e1);
}
- for (off = 0x80000; off + 8 <= vgpu->bars[1].size && off < 0x200000;
+ for (off = 0x80000; off + 8 <= svc_fb_shadow_size(vgpu) && off < 0x200000;
off += 0x1000) {
u32 w0, w1;
@@ -14927,7 +14954,7 @@ int gsp_sema_service_dump_vidmem(u64 addr, u64 len)
return -ENODEV;
base = vgpu->bars[1].vaddr;
- if (!base || addr + len > vgpu->bars[1].size)
+ if (!base || addr + len > svc_fb_shadow_size(vgpu))
return -EINVAL;
pr_info("sema-vidmem: dump addr=0x%llx len=%llu\n",
@@ -14961,7 +14988,7 @@ int gsp_sema_service_find_vidmem(u64 pattern, u64 max_hits)
return -ENODEV;
base = vgpu->bars[1].vaddr;
- size = vgpu->bars[1].size;
+ size = svc_fb_shadow_size(vgpu);
if (!base)
return -EINVAL;
if (!max_hits)