]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
idr: fix idr_alloc() returning an ID out of range
authorMatthew Wilcox (Oracle) <willy@infradead.org>
Fri, 28 Nov 2025 16:18:32 +0000 (16:18 +0000)
committerAndrew Morton <akpm@linux-foundation.org>
Tue, 23 Dec 2025 19:23:11 +0000 (11:23 -0800)
If you use an IDR with a non-zero base, and specify a range that lies
entirely below the base, 'max - base' becomes very large and
idr_get_free() can return an ID that lies outside of the requested range.

Link: https://lkml.kernel.org/r/20251128161853.3200058-1-willy@infradead.org
Fixes: 6ce711f27500 ("idr: Make 1-based IDRs more efficient")
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reported-by: Jan Sokolowski <jan.sokolowski@intel.com>
Reported-by: Koen Koning <koen.koning@intel.com>
Reported-by: Peter Senna Tschudin <peter.senna@linux.intel.com>
Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/6449
Reviewed-by: Christian König <christian.koenig@amd.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
lib/idr.c
tools/testing/radix-tree/idr-test.c

index e2adc457abb4be7af947dc71f6414036f78128ad..457430cff8c5e1eaca3777fc90a36a7f02f854df 100644 (file)
--- a/lib/idr.c
+++ b/lib/idr.c
@@ -40,6 +40,8 @@ int idr_alloc_u32(struct idr *idr, void *ptr, u32 *nextid,
 
        if (WARN_ON_ONCE(!(idr->idr_rt.xa_flags & ROOT_IS_IDR)))
                idr->idr_rt.xa_flags |= IDR_RT_MARKER;
+       if (max < base)
+               return -ENOSPC;
 
        id = (id < base) ? 0 : id - base;
        radix_tree_iter_init(&iter, id);
index 2f830ff8396cc45d63f2ce70224f79270426f026..945144e985072435d30f3c727a83a7ab794dd89d 100644 (file)
@@ -57,6 +57,26 @@ void idr_alloc_test(void)
        idr_destroy(&idr);
 }
 
+void idr_alloc2_test(void)
+{
+       int id;
+       struct idr idr = IDR_INIT_BASE(idr, 1);
+
+       id = idr_alloc(&idr, idr_alloc2_test, 0, 1, GFP_KERNEL);
+       assert(id == -ENOSPC);
+
+       id = idr_alloc(&idr, idr_alloc2_test, 1, 2, GFP_KERNEL);
+       assert(id == 1);
+
+       id = idr_alloc(&idr, idr_alloc2_test, 0, 1, GFP_KERNEL);
+       assert(id == -ENOSPC);
+
+       id = idr_alloc(&idr, idr_alloc2_test, 0, 2, GFP_KERNEL);
+       assert(id == -ENOSPC);
+
+       idr_destroy(&idr);
+}
+
 void idr_replace_test(void)
 {
        DEFINE_IDR(idr);
@@ -409,6 +429,7 @@ void idr_checks(void)
 
        idr_replace_test();
        idr_alloc_test();
+       idr_alloc2_test();
        idr_null_test();
        idr_nowait_test();
        idr_get_next_test(0);