On 23/01/2026 23:42, Harshit Mogalapalli wrote:
Hi,

On 23/01/26 19:45, Tvrtko Ursulin wrote:
Since GEM bo handles are u32 in the uapi and the internal implementation
uses idr_alloc() which uses int ranges, passing a new handle larger than
INT_MAX trivially triggers a kernel warning:

idr_alloc():
...
    if (WARN_ON_ONCE(start < 0))
        return -EINVAL;
...

Fix it by rejecting new handles above INT_MAX and at the same time make
the end limit calculation more obvious by moving into int domain.

Signed-off-by: Tvrtko Ursulin <[email protected]>
Reported-by: Zhi Wang <[email protected]>
Fixes: 53096728b891 ("drm: Add DRM prime interface to reassign GEM handle")
Cc: David Francis <[email protected]>
Cc: Felix Kuehling <[email protected]>
Cc: Christian König <[email protected]>
Cc: <[email protected]> # v6.18+


Thanks,

I have seen this WARN_ON as well and I have tested the reproducer against your patch and it works.

So:

Tested-by: Harshit Mogalapalli <[email protected]>

Thank you! May I ask what test cases are you using to exercise it?


A question below:

---
v2:
  * Rename local variable, re-position comment, drop the else block. (Christian)
  * Use local at more places.
---
  drivers/gpu/drm/drm_gem.c | 18 ++++++++++++------
  1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index 7ff6b7bbeb73..ffa7852c8f6c 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -1001,16 +1001,21 @@ int drm_gem_change_handle_ioctl(struct drm_device *dev, void *data,
  {
      struct drm_gem_change_handle *args = data;
      struct drm_gem_object *obj;
-    int ret;
+    int handle, ret;
      if (!drm_core_check_feature(dev, DRIVER_GEM))
          return -EOPNOTSUPP;
+    /* idr_alloc() limitation. */
+    if (args->new_handle > INT_MAX)
+        return -EINVAL;

INT_MAX is allowed.

+    handle = args->new_handle;
+
      obj = drm_gem_object_lookup(file_priv, args->handle);
      if (!obj)
          return -ENOENT;
-    if (args->handle == args->new_handle) {
+    if (args->handle == handle) {
          ret = 0;
          goto out;
      }
@@ -1018,18 +1023,19 @@ int drm_gem_change_handle_ioctl(struct drm_device *dev, void *data,
      mutex_lock(&file_priv->prime.lock);
      spin_lock(&file_priv->table_lock);
-    ret = idr_alloc(&file_priv->object_idr, obj,
-        args->new_handle, args->new_handle + 1, GFP_NOWAIT);
+    ret = idr_alloc(&file_priv->object_idr, obj, handle, handle + 1,

handle + 1 here would cause a signed integer overflow ?

For the kernel it is fine due -fno-strict-overflow and idr_alloc() explicitly handles it:

...
 * Allocates an unused ID in the range specified by @start and @end.  If
 * @end is <= 0, it is treated as one larger than %INT_MAX.  This allows
 * callers to use @start + N as @end as long as N is within integer range.
...
        ret = idr_alloc_u32(idr, ptr, &id, end > 0 ? end - 1 : INT_MAX, gfp);

So for start == INT_MAX it ends up passing end == INT_MAX to idr_alloc_u32, which, contrary to idr_alloc(), has it's end range parameter _inclusive_.

Simple huh? :))

Regards,

Tvrtko




Thanks,
Harshit
+            GFP_NOWAIT);
      spin_unlock(&file_priv->table_lock);
      if (ret < 0)
          goto out_unlock;
      if (obj->dma_buf) {
-        ret = drm_prime_add_buf_handle(&file_priv->prime, obj- >dma_buf, args->new_handle);
+        ret = drm_prime_add_buf_handle(&file_priv->prime, obj->dma_buf,
+                           handle);
          if (ret < 0) {
              spin_lock(&file_priv->table_lock);
-            idr_remove(&file_priv->object_idr, args->new_handle);
+            idr_remove(&file_priv->object_idr, handle);
              spin_unlock(&file_priv->table_lock);
              goto out_unlock;
          }


Reply via email to