From: syzbot <syzbot+95416f957d84e858b377@syzkaller.appspotmail.com>
To: linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com
Subject: Forwarded: [PATCH v2] drm/syncobj: Limit the number of handles and use kvmalloc for user-sized arrays
Date: Tue, 03 Nov 2026 18:31:10 -0800 [thread overview]
Message-ID: <6aea98ee.0b257d1c.25e26f.0003.GAE@google.com> (raw)
In-Reply-To: <000000000000f6bf6d0604af3b95@google.com>
For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.
***
Subject: [PATCH v2] drm/syncobj: Limit the number of handles and use kvmalloc for user-sized arrays
Author: fei.yu@intel.com
From: Fei Yu <fei.yu@intel.com>
#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master
The syncobj ioctls allocate several temporary arrays whose size is taken
directly from the u32 count_handles provided by userspace:
- handles and syncobjs in drm_syncobj_array_find()
- points and entries in drm_syncobj_array_wait_timeout()
- points and chains in drm_syncobj_timeline_signal_ioctl()
These are kmalloc() allocations, so a count above KMALLOC_MAX_SIZE /
element size trips the WARN_ON_ONCE_GFP() in ___kmalloc_large_node():
WARNING: mm/slub.c:5459 at ___kmalloc_large_node+0x10c/0x170 mm/slub.c:5459
Call Trace:
__kmalloc_large_node_noprof+0x18/0x90 mm/slub.c:5502
__do_kmalloc_node mm/slub.c:5519 [inline]
__kmalloc_noprof+0x3f6/0x710 mm/slub.c:5556
_kmalloc_noprof include/linux/slab.h:995 [inline]
_kmalloc_array_noprof include/linux/slab.h:1211 [inline]
drm_syncobj_array_find+0x3a/0x440 drivers/gpu/drm/drm_syncobj.c:1241
drm_syncobj_timeline_wait_ioctl+0x18a/0x640 drivers/gpu/drm/drm_syncobj.c:1356
drm_ioctl_kernel+0x2df/0x3b0 drivers/gpu/drm/drm_ioctl.c:817
drm_ioctl+0x70e/0xba0 drivers/gpu/drm/drm_ioctl.c:914
The wait entries are 64 bytes each on 64-bit, so with a 4 MiB
KMALLOC_MAX_SIZE a wait on more than about 64K handles already fails
today, with a warning.
Fix this in two parts:
- Reject more than DRM_SYNCOBJ_MAX_HANDLES handles with -EINVAL in
drm_syncobj_array_find(), which every ioctl taking a handle array goes
through. The limit is derived from the largest per-handle allocation
(the wait entries) and the INT_MAX size limit of kvmalloc(), so none
of the allocations above can exceed it. That is about 33M handles on
64-bit.
- Switch the six allocations to kvmalloc(), so that large but valid
handle counts no longer depend on physically contiguous memory.
Using kvmalloc() alone is not enough: the reproducer asks for more than
INT_MAX bytes, which hits the WARN_ON_ONCE() in __kvmalloc_node_noprof()
instead.
Fixes: 3e6fb72d6cef ("drm/syncobj: Add a syncobj_array_find helper")
Reported-by: syzbot+95416f957d84e858b377@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=95416f957d84e858b377
Suggested-by: Christian König <christian.koenig@amd.com>
Signed-off-by: Fei Yu <fei.yu@intel.com>
---
v2:
- Drop __GFP_NOWARN (Christian).
- Add DRM_SYNCOBJ_MAX_HANDLES, return -EINVAL above it, and switch all six
arrays to kvmalloc() (Christian).
- v1: https://lore.kernel.org/all/20261103085002.3100723-1-fei.yu@intel.com/
About the limit: INT_MAX / sizeof(struct syncobj_wait_entry) is about 33M
handles on 64-bit. It is the largest count for which every allocation stays
below the kvmalloc() INT_MAX size limit, so it does not add a new policy
number. Today the real limit is much lower: a wait on more than ~64K
handles already fails with -ENOMEM (plus the warning), because the wait
entries are a kmalloc() allocation larger than 4 MiB. If you prefer a lower,
fixed limit (e.g. SZ_1M) or GFP_KERNEL_ACCOUNT for these arrays, I can
change it.
Testing on v7.4-rc1 (x86_64, KASAN, virtio-gpu in QEMU):
- syzbot C reproducer: base warns in ___kmalloc_large_node() (size
0x100000920), patched is clean, ioctl returns -EINVAL.
- wait / timeline_wait / timeline_signal / reset / signal on 200000
signaled syncobjs: base fails wait and timeline_wait with -ENOMEM and
the same warning; patched all succeed.
- count == DRM_SYNCOBJ_MAX_HANDLES gets past the check (-EFAULT from the
short user buffer), count == DRM_SYNCOBJ_MAX_HANDLES + 1 returns -EINVAL
for wait, reset and timeline_signal.
- kmemleak clean, checkpatch --strict clean, W=1 clean,
DRM KUnit 667/667 passed.
- IGT syncobj_basic, syncobj_wait, syncobj_timeline on virtio-gpu:
202/202 subtests pass, same as base.
drivers/gpu/drm/drm_syncobj.c | 40 ++++++++++++++++++++++-------------
1 file changed, 25 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/drm_syncobj.c b/drivers/gpu/drm/drm_syncobj.c
index f1d4da1685457..c01cf164a4f9e 100644
--- a/drivers/gpu/drm/drm_syncobj.c
+++ b/drivers/gpu/drm/drm_syncobj.c
@@ -220,6 +220,13 @@ struct syncobj_wait_entry {
u64 point;
};
+/*
+ * Upper limit for the number of handles passed to a single ioctl. The
+ * largest per-handle allocation is the wait entry array, keep it below the
+ * INT_MAX size limit of kvmalloc().
+ */
+#define DRM_SYNCOBJ_MAX_HANDLES (INT_MAX / sizeof(struct syncobj_wait_entry))
+
static void syncobj_wait_syncobj_func(struct drm_syncobj *syncobj,
struct syncobj_wait_entry *wait);
@@ -1051,7 +1058,7 @@ static signed long drm_syncobj_array_wait_timeout(struct drm_syncobj **syncobjs,
lockdep_assert_none_held_once();
}
- points = kmalloc_array(count, sizeof(*points), GFP_KERNEL);
+ points = kvmalloc_array(count, sizeof(*points), GFP_KERNEL);
if (points == NULL)
return -ENOMEM;
@@ -1064,7 +1071,7 @@ static signed long drm_syncobj_array_wait_timeout(struct drm_syncobj **syncobjs,
goto err_free_points;
}
- entries = kzalloc_objs(*entries, count);
+ entries = kvzalloc_objs(*entries, count);
if (!entries) {
timeout = -ENOMEM;
goto err_free_points;
@@ -1185,10 +1192,10 @@ static signed long drm_syncobj_array_wait_timeout(struct drm_syncobj **syncobjs,
&entries[i].fence_cb);
dma_fence_put(entries[i].fence);
}
- kfree(entries);
+ kvfree(entries);
err_free_points:
- kfree(points);
+ kvfree(points);
return timeout;
}
@@ -1238,7 +1245,10 @@ static int drm_syncobj_array_find(struct drm_file *file_private,
struct drm_syncobj **syncobjs;
int ret;
- handles = kmalloc_array(count_handles, sizeof(*handles), GFP_KERNEL);
+ if (count_handles > DRM_SYNCOBJ_MAX_HANDLES)
+ return -EINVAL;
+
+ handles = kvmalloc_array(count_handles, sizeof(*handles), GFP_KERNEL);
if (handles == NULL)
return -ENOMEM;
@@ -1248,7 +1258,7 @@ static int drm_syncobj_array_find(struct drm_file *file_private,
goto err_free_handles;
}
- syncobjs = kmalloc_objs(*syncobjs, count_handles);
+ syncobjs = kvmalloc_objs(*syncobjs, count_handles);
if (syncobjs == NULL) {
ret = -ENOMEM;
goto err_free_handles;
@@ -1262,16 +1272,16 @@ static int drm_syncobj_array_find(struct drm_file *file_private,
}
}
- kfree(handles);
+ kvfree(handles);
*syncobjs_out = syncobjs;
return 0;
err_put_syncobjs:
while (i-- > 0)
drm_syncobj_put(syncobjs[i]);
- kfree(syncobjs);
+ kvfree(syncobjs);
err_free_handles:
- kfree(handles);
+ kvfree(handles);
return ret;
}
@@ -1283,7 +1293,7 @@ static void drm_syncobj_array_free(struct drm_syncobj **syncobjs,
for (i = 0; i < count; i++)
drm_syncobj_put(syncobjs[i]);
- kfree(syncobjs);
+ kvfree(syncobjs);
}
int
@@ -1572,8 +1582,8 @@ drm_syncobj_timeline_signal_ioctl(struct drm_device *dev, void *data,
if (ret < 0)
return ret;
- points = kmalloc_array(args->count_handles, sizeof(*points),
- GFP_KERNEL);
+ points = kvmalloc_array(args->count_handles, sizeof(*points),
+ GFP_KERNEL);
if (!points) {
ret = -ENOMEM;
goto out;
@@ -1586,7 +1596,7 @@ drm_syncobj_timeline_signal_ioctl(struct drm_device *dev, void *data,
goto err_points;
}
- chains = kmalloc_objs(*chains, args->count_handles);
+ chains = kvmalloc_objs(*chains, args->count_handles);
if (!chains) {
ret = -ENOMEM;
goto err_points;
@@ -1609,9 +1619,9 @@ drm_syncobj_timeline_signal_ioctl(struct drm_device *dev, void *data,
dma_fence_put(fence);
}
err_chains:
- kfree(chains);
+ kvfree(chains);
err_points:
- kfree(points);
+ kvfree(points);
out:
drm_syncobj_array_free(syncobjs, args->count_handles);
base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
--
2.43.0
next prev parent reply other threads:[~2026-11-04 2:31 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-09-06 11:42 [syzbot] [dri?] WARNING in drm_syncobj_array_find syzbot
2026-11-03 8:23 ` Forwarded: [PATCH] drm/syncobj: Silence allocation warnings for user-sized arrays syzbot
2026-11-04 2:31 ` syzbot [this message]
2026-11-04 2:37 ` Forwarded: [PATCH v2] drm/syncobj: Limit the number of handles and use kvmalloc " syzbot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=6aea98ee.0b257d1c.25e26f.0003.GAE@google.com \
--to=syzbot+95416f957d84e858b377@syzkaller.appspotmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=syzkaller-bugs@googlegroups.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®