From 6064894dad5e4238543bf779269ad3fbfaa20add Mon Sep 17 00:00:00 2001 From: Vaggelis Date: Mon, 24 Aug 2026 11:48:23 -0400 Subject: [PATCH] nvidia-drm: reject out-of-bounds semaphore surface indices DRM_IOCTL_NVIDIA_SEMSURF_FENCE_CTX_CREATE is reachable from unprivileged render node clients and passes a 64-bit semaphore index that __nv_drm_semsurf_fence_ctx_new() used unchecked to shift the CPU mappings of the imported surface by index * stride. NVKMS maps exactly the surface size reported by the import parameters, but never exposed that size to nvidia-drm, so there was nothing to validate against. A later fence operation then reads the semaphore and max-submitted values through the shifted pointers, so any index at or beyond the end of the mapped surface makes the kernel read outside of it. importSemaphoreSurface() now returns the mapped size through a new out parameter, and the context constructor rejects indices outside the surface, indices above NV_U32_MAX which the RM semaphore index field cannot represent, and layouts whose max-submitted offset does not fit in one stride, before any pointer arithmetic happens. Test Plan: No build or runtime test possible in the preparation environment (no Linux kernel toolchain, no NVIDIA GPU). Verified statically: both KAPI header copies updated identically; nvKmsKapiImportSemaphoreSurface prototype and implementation agree with the table assignment; validation runs after a successful import and before the first shifted dereference; the rejection path frees the imported surface via failed_alloc_fence_context; stride == 0 is rejected before the division so unsupported configurations cannot divide by zero. --- kernel-open/common/inc/nvkms-kapi.h | 6 ++++- kernel-open/nvidia-drm/nvidia-drm-fence.c | 25 ++++++++++++++++++- .../kapi/include/nvkms-kapi-internal.h | 3 ++- .../kapi/interface/nvkms-kapi.h | 6 ++++- src/nvidia-modeset/kapi/src/nvkms-kapi-sync.c | 7 +++++- 5 files changed, 42 insertions(+), 5 deletions(-) diff --git a/kernel-open/common/inc/nvkms-kapi.h b/kernel-open/common/inc/nvkms-kapi.h index 8e838eafa9..9b292c183a 100644 --- a/kernel-open/common/inc/nvkms-kapi.h +++ b/kernel-open/common/inc/nvkms-kapi.h @@ -1357,6 +1357,9 @@ struct NvKmsKapiFunctionsTable { * \param [out] pMaxSubmittedMap Returns a CPU mapping of the semaphore * surface's semaphore memory to the client. * + * \param [out] pSurfaceSize Returns the size of the imported semaphore + * surface in bytes. May be NULL. + * * \return struct NvKmsKapiSemaphoreSurface* on success, NULL on failure. */ struct NvKmsKapiSemaphoreSurface* (*importSemaphoreSurface) @@ -1365,7 +1368,8 @@ struct NvKmsKapiFunctionsTable { NvU64 nvKmsParamsUser, NvU64 nvKmsParamsSize, void **pSemaphoreMap, - void **pMaxSubmittedMap + void **pMaxSubmittedMap, + NvU64 *pSurfaceSize ); /*! diff --git a/kernel-open/nvidia-drm/nvidia-drm-fence.c b/kernel-open/nvidia-drm/nvidia-drm-fence.c index 7af1ed7f13..7483de1ef7 100644 --- a/kernel-open/nvidia-drm/nvidia-drm-fence.c +++ b/kernel-open/nvidia-drm/nvidia-drm-fence.c @@ -1231,13 +1231,15 @@ __nv_drm_semsurf_fence_ctx_new( struct NvKmsKapiSemaphoreSurface *pSemSurface; uint8_t *semMapping; uint8_t *maxSubmittedMapping; + NvU64 surfaceSize = 0; char worker_name[20+16+1]; /* strlen(nvidia-drm/timeline-) + 16 for %llx + NUL */ pSemSurface = nvKms->importSemaphoreSurface(nv_dev->pDevice, p->nvkms_params_ptr, p->nvkms_params_size, (void **)&semMapping, - (void **)&maxSubmittedMapping); + (void **)&maxSubmittedMapping, + &surfaceSize); if (!pSemSurface) { NV_DRM_DEV_LOG_ERR( nv_dev, @@ -1246,6 +1248,27 @@ __nv_drm_semsurf_fence_ctx_new( goto failed; } + /* + * The index is provided by userspace as a 64-bit value. Reject values + * outside the imported surface before shifting the CPU mappings by them, + * and before truncating to the 32-bit RM semaphore index below. The + * max-submitted value must fit within one stride so that indexing the + * semaphore mapping also bounds the max-submitted mapping. + */ + if (nv_dev->semsurf_stride == 0 || + (nv_dev->semsurf_max_submitted_offset + sizeof(NvU64)) > + nv_dev->semsurf_stride || + p->index >= (surfaceSize / nv_dev->semsurf_stride) || + p->index > NV_U32_MAX) { + NV_DRM_DEV_LOG_ERR( + nv_dev, + "Invalid semaphore index %" NvU64_fmtu " for semaphore surface of %" + NvU64_fmtu " bytes", + p->index, surfaceSize); + + goto failed_alloc_fence_context; + } + /* * Allocate a fence context object and initialize it. */ diff --git a/src/nvidia-modeset/kapi/include/nvkms-kapi-internal.h b/src/nvidia-modeset/kapi/include/nvkms-kapi-internal.h index 1757f13953..0fcc89328f 100644 --- a/src/nvidia-modeset/kapi/include/nvkms-kapi-internal.h +++ b/src/nvidia-modeset/kapi/include/nvkms-kapi-internal.h @@ -237,7 +237,8 @@ nvKmsKapiImportSemaphoreSurface(struct NvKmsKapiDevice *device, NvU64 nvKmsParamsUser, NvU64 nvKmsParamsSize, void **pSemaphoreMap, - void **pMaxSubmittedMap); + void **pMaxSubmittedMap, + NvU64 *pSurfaceSize); void nvKmsKapiFreeSemaphoreSurface(struct NvKmsKapiDevice *device, diff --git a/src/nvidia-modeset/kapi/interface/nvkms-kapi.h b/src/nvidia-modeset/kapi/interface/nvkms-kapi.h index 8e838eafa9..9b292c183a 100644 --- a/src/nvidia-modeset/kapi/interface/nvkms-kapi.h +++ b/src/nvidia-modeset/kapi/interface/nvkms-kapi.h @@ -1357,6 +1357,9 @@ struct NvKmsKapiFunctionsTable { * \param [out] pMaxSubmittedMap Returns a CPU mapping of the semaphore * surface's semaphore memory to the client. * + * \param [out] pSurfaceSize Returns the size of the imported semaphore + * surface in bytes. May be NULL. + * * \return struct NvKmsKapiSemaphoreSurface* on success, NULL on failure. */ struct NvKmsKapiSemaphoreSurface* (*importSemaphoreSurface) @@ -1365,7 +1368,8 @@ struct NvKmsKapiFunctionsTable { NvU64 nvKmsParamsUser, NvU64 nvKmsParamsSize, void **pSemaphoreMap, - void **pMaxSubmittedMap + void **pMaxSubmittedMap, + NvU64 *pSurfaceSize ); /*! diff --git a/src/nvidia-modeset/kapi/src/nvkms-kapi-sync.c b/src/nvidia-modeset/kapi/src/nvkms-kapi-sync.c index 1b243d64e6..09fa698b10 100644 --- a/src/nvidia-modeset/kapi/src/nvkms-kapi-sync.c +++ b/src/nvidia-modeset/kapi/src/nvkms-kapi-sync.c @@ -185,7 +185,8 @@ nvKmsKapiImportSemaphoreSurface NvU64 nvKmsParamsUser, NvU64 nvKmsParamsSize, void **pSemaphoreMap, - void **pMaxSubmittedMap + void **pMaxSubmittedMap, + NvU64 *pSurfaceSize ) { struct NvKmsKapiSemaphoreSurface *ss = NULL; @@ -311,6 +312,10 @@ nvKmsKapiImportSemaphoreSurface *pMaxSubmittedMap = NULL; } + if (pSurfaceSize != NULL) { + *pSurfaceSize = p.semaphoreSurfaceSize; + } + return ss; fail: