[PATCH] drm/etnaviv: reject read-only userptr BOs as perfmon targets
From: Junghyun Park
Date: Thu Sep 17 2026 - 04:12:37 EST
submit_perfmon_validate() lets a submission name any buffer object (BO)
as the target for perfmon results, but the kernel writes those results
back into the BO on job completion: "*pmr->bo_vma = pmr->sequence" in
sync_point_perfmon_sample_post() and the counter store in
etnaviv_perfmon_process(). It never checks that the BO was submitted
writable.
An unprivileged user can register a read-only userptr BO backed by the
page-cache page of a file it can only read, via
DRM_IOCTL_ETNAVIV_GEM_USERPTR (userptr.ro = true, pinned without
FOLL_WRITE), then name it as a perfmon target in
DRM_IOCTL_ETNAVIV_GEM_SUBMIT. etnaviv_gem_vmap_impl() maps the CACHED
BO PAGE_KERNEL (writable) regardless of userptr.ro, so pmr->bo_vma
becomes a writable kernel alias of the read-only page. On completion
the kernel stores a caller-controlled 32-bit value into it, overwriting
the in-memory page-cache contents of a file the caller can only read.
All etnaviv ioctls are DRM_RENDER_ALLOW, so no elevated privilege is
required.
Reject a read-only userptr BO named as a perfmon result target. The
submit-time BO_WRITE flag is not usable for this: it is caller-supplied
and describes GPU access, so it neither reflects nor constrains how the
userptr pages were pinned.
Fixes: 249300c740e5 ("drm/etnaviv: add performance monitor request processing")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: Codex:gpt-5
Signed-off-by: Junghyun Park <mastpark2001@xxxxxxxxx>
---
drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c
index 1a77a09b3..e44fa40fd 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c
@@ -322,6 +322,13 @@ static int submit_perfmon_validate(struct etnaviv_gem_submit *submit,
if (ret)
return ret;
+ /* perfmon writes the result back; refuse a read-only userptr */
+ if (bo->obj->userptr.ro) {
+ DRM_ERROR("perfmon request: BO %u is a read-only userptr\n",
+ r->read_idx);
+ return -EINVAL;
+ }
+
/* at offset 0 a sequence number gets stored used for userspace sync */
if (r->read_offset == 0) {
DRM_ERROR("perfmon request: offset is 0");