Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

idr: fix idr_alloc() returning an ID out of range

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>

authored by

Matthew Wilcox (Oracle) and committed by
Andrew Morton
c6e8e595 87726567

+23
+2
lib/idr.c
··· 40 40 41 41 if (WARN_ON_ONCE(!(idr->idr_rt.xa_flags & ROOT_IS_IDR))) 42 42 idr->idr_rt.xa_flags |= IDR_RT_MARKER; 43 + if (max < base) 44 + return -ENOSPC; 43 45 44 46 id = (id < base) ? 0 : id - base; 45 47 radix_tree_iter_init(&iter, id);
+21
tools/testing/radix-tree/idr-test.c
··· 57 57 idr_destroy(&idr); 58 58 } 59 59 60 + void idr_alloc2_test(void) 61 + { 62 + int id; 63 + struct idr idr = IDR_INIT_BASE(idr, 1); 64 + 65 + id = idr_alloc(&idr, idr_alloc2_test, 0, 1, GFP_KERNEL); 66 + assert(id == -ENOSPC); 67 + 68 + id = idr_alloc(&idr, idr_alloc2_test, 1, 2, GFP_KERNEL); 69 + assert(id == 1); 70 + 71 + id = idr_alloc(&idr, idr_alloc2_test, 0, 1, GFP_KERNEL); 72 + assert(id == -ENOSPC); 73 + 74 + id = idr_alloc(&idr, idr_alloc2_test, 0, 2, GFP_KERNEL); 75 + assert(id == -ENOSPC); 76 + 77 + idr_destroy(&idr); 78 + } 79 + 60 80 void idr_replace_test(void) 61 81 { 62 82 DEFINE_IDR(idr); ··· 429 409 430 410 idr_replace_test(); 431 411 idr_alloc_test(); 412 + idr_alloc2_test(); 432 413 idr_null_test(); 433 414 idr_nowait_test(); 434 415 idr_get_next_test(0);