[PATCH v5] drm/xe: Add bounds check for num_binds to prevent memory exhaustion
Ramesh Adhikari <[email protected]> Tue, 4 Aug 2026 11:06:00 +0530
| Newsgroups | org.freedesktop.lists.intel-xe,org.kernel.vger.stable |
|---|---|
| Message-ID | <[email protected]> |
The xe_vm_bind_ioctl function accepts user-controlled num_binds without
any bounds checking. This follows the same pattern that was fixed for
num_syncs in commit 8e461304009d ("drm/xe: Limit num_syncs to prevent
huge allocations").
While the main allocations (bind_ops, bos, ops arrays) use __GFP_ACCOUNT,
vm_bind_ioctl_ops_create makes additional allocations in a loop that don't:
- drm_gpuva_ops (16 bytes) at drm_gpuvm.c:2949
- xe_vma_op (144 bytes) at xe_vm.c:1318
Both use kzalloc_obj() which defaults to GFP_KERNEL without __GFP_ACCOUNT.
For 268M binds, the loop runs 268M times, allocating 160 bytes per iteration.
That's about 43 GB allocated without cgroup accounting before the code even
hits the main allocation (which will fail due to the 4MB kmalloc limit).
Add DRM_XE_MAX_BINDS (65536) limit, checked before any allocations happen.
At 65536 binds, we're allocating ~10MB in the loop, which is reasonable and
won't force unnecessary fallbacks in userspace.
Return -ENOBUFS instead of -EINVAL so Mesa can retry with smaller batches
if needed.
Note on v3/v4: I sent v3 twice (2026-05-07, 2026-05-08) and v4
(2026-05-09) addressing the feedback below, but none of them appear to
have reached the list or Patchwork - likely a subscription/moderation
issue on my end that should now be resolved. Resending the same content
as v5 in case v3/v4 never arrived:
v4: [email protected] (2026-05-09)
v3: [email protected] (2026-05-08)
v3: (resend, 2026-05-07, same content)
v4: Increased limit to 65536 (64k) per maintainer feedback, changed error
to -ENOBUFS for graceful retry
v3: Changed to -ENOBUFS, moved check earlier, added allocation analysis
v2: Increased limit from 1024 to 2048 after Mesa source analysis
Cc: [email protected]
Signed-off-by: Ramesh Adhikari <[email protected]>
---
drivers/gpu/drm/xe/xe_vm.c | 7 +++++++
include/uapi/drm/xe_drm.h | 1 +
2 files changed, 8 insertions(+)
diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index a717a2b8dea..1ab020cbdc1 100644
--- a/drivers/gpu/drm/xe/xe_vm.c
+++ b/drivers/gpu/drm/xe/xe_vm.c
@@ -3840,7 +3840,14 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
if (XE_IOCTL_DBG(xe, !vm))
return -EINVAL;
+ /* Prevent unbounded allocations in vm_bind_ioctl_ops_create loop */
+ if (XE_IOCTL_DBG(xe, args->num_binds > DRM_XE_MAX_BINDS)) {
+ err = -ENOBUFS;
+ goto put_vm;
+ }
+
err = vm_bind_ioctl_check_args(xe, vm, args, &bind_ops);
+
if (err)
goto put_vm;
diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h
index ae2fda23ce7..33f23f6638e 100644
--- a/include/uapi/drm/xe_drm.h
+++ b/include/uapi/drm/xe_drm.h
@@ -1606,6 +1606,7 @@ struct drm_xe_exec {
__u32 exec_queue_id;
#define DRM_XE_MAX_SYNCS 1024
+#define DRM_XE_MAX_BINDS 65536
/** @num_syncs: Amount of struct drm_xe_sync in array. */
__u32 num_syncs;
--
2.43.0