CVE-2023-6915: NULL Pointer Dereference (CWE-476)
value: A Null pointer dereference problem was found in ida_free in lib/idr.c in the Linux Kernel. This issue may allow an attacker using this library to cause a denial of service problem due to a missing check at a function return.
CWE description: The product dereferences a pointer that it expects to be valid but is NULL.
CVSS v3 base score: 5.5
Published: 2024-01-15T10:15Z
Repository: https://github.com/torvalds/linux
Language: C
Files changed: lib/idr.c, lib/test_ida.c
Commit date: 2023-12-21 10:02:28 -0800
Commit message: ida: Fix crash in ida_free when the bitmap is empty

The IDA usually detects double-frees, but that detection failed to
consider the case when there are no nearby IDs allocated and so we have a
NULL bitmap rather than simply having a clear bit.  Add some tests to the
test-suite to be sure we don't inadvertently reintroduce this problem.
Unfortunately they're quite noisy so include a message to disregard
the warnings.

Reported-by: Zhenghan Wang <wzhmmmmm@gmail.com>
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Diff: diff --git a/lib/idr.c b/lib/idr.c
index 13f2758c2377..da36054c3ca0 100644
--- a/lib/idr.c
+++ b/lib/idr.c
@@ -506,11 +506,11 @@ void ida_free(struct ida *ida, unsigned int id)
 		v &= ~(1UL << bit);
 		if (!v)
 			goto delete;
 		xas_store(&xas, xa_mk_value(v));
 	} else {
-		if (!test_bit(bit, bitmap->bitmap))
+		if (!bitmap || !test_bit(bit, bitmap->bitmap))
 			goto err;
 		__clear_bit(bit, bitmap->bitmap);
 		xas_set_mark(&xas, XA_FREE_MARK);
 		if (bitmap_empty(bitmap->bitmap, IDA_BITMAP_BITS)) {
 			kfree(bitmap);
diff --git a/lib/test_ida.c b/lib/test_ida.c
index b06880625961..55105baa19da 100644
--- a/lib/test_ida.c
+++ b/lib/test_ida.c
@@ -148,10 +148,49 @@ static void ida_check_conv(struct ida *ida)
 	for (i = IDA_BITMAP_BITS + BITS_PER_LONG - 4; i > 0; i--)
 		ida_free(ida, i - 1);
 	IDA_BUG_ON(ida, !ida_is_empty(ida));
 }
 
+/*
+ * Check various situations where we attempt to free an ID we don't own.
+ */
+static void ida_check_bad_free(struct ida *ida)
+{
+	unsigned long i;
+
+	printk("vvv Ignore \"not allocated\" warnings\n");
+	/* IDA is empty; all of these will fail */
+	ida_free(ida, 0);
+	for (i = 0; i < 31; i++)
+		ida_free(ida, 1 << i);
+
+	/* IDA contains a single value entry */
+	IDA_BUG_ON(ida, ida_alloc_min(ida, 3, GFP_KERNEL) != 3);
+	ida_free(ida, 0);
+	for (i = 0; i < 31; i++)
+		ida_free(ida, 1 << i);
+
+	/* IDA contains a single bitmap */
+	IDA_BUG_ON(ida, ida_alloc_min(ida, 1023, GFP_KERNEL) != 1023);
+	ida_free(ida, 0);
+	for (i = 0; i < 31; i++)
+		ida_free(ida, 1 << i);
+
+	/* IDA contains a tree */
+	IDA_BUG_ON(ida, ida_alloc_min(ida, (1 << 20) - 1, GFP_KERNEL) != (1 << 20) - 1);
+	ida_free(ida, 0);
+	for (i = 0; i < 31; i++)
+		ida_free(ida, 1 << i);
+	printk("^^^ \"not allocated\" warnings over\n");
+
+	ida_free(ida, 3);
+	ida_free(ida, 1023);
+	ida_free(ida, (1 << 20) - 1);
+
+	IDA_BUG_ON(ida, !ida_is_empty(ida));
+}
+
 static DEFINE_IDA(ida);
 
 static int ida_checks(void)
 {
 	IDA_BUG_ON(&ida, !ida_is_empty(&ida));
@@ -160,10 +199,11 @@ static int ida_checks(void)
 	ida_check_leaf(&ida, 0);
 	ida_check_leaf(&ida, 1024);
 	ida_check_leaf(&ida, 1024 * 64);
 	ida_check_max(&ida);
 	ida_check_conv(&ida);
+	ida_check_bad_free(&ida);
 
 	printk("IDA: %u of %u tests passed\n", tests_passed, tests_run);
 	return (tests_run != tests_passed) ? 0 : -EINVAL;
 }
 

Vulnerable code: 		if (!test_bit(bit, bitmap->bitmap))
Fixed code: 		if (!bitmap || !test_bit(bit, bitmap->bitmap))
/*
 * Check various situations where we attempt to free an ID we don't own.
 */
static void ida_check_bad_free(struct ida *ida)
{
	unsigned long i;

	printk("vvv Ignore \"not allocated\" warnings\n");
	/* IDA is empty; all of these will fail */
	ida_free(ida, 0);
	for (i = 0; i < 31; i++)
		ida_free(ida, 1 << i);

	/* IDA contains a single value entry */
	IDA_BUG_ON(ida, ida_alloc_min(ida, 3, GFP_KERNEL) != 3);
	ida_free(ida, 0);
	for (i = 0; i < 31; i++)
		ida_free(ida, 1 << i);

	/* IDA contains a single bitmap */
	IDA_BUG_ON(ida, ida_alloc_min(ida, 1023, GFP_KERNEL) != 1023);
	ida_free(ida, 0);
	for (i = 0; i < 31; i++)
		ida_free(ida, 1 << i);

	/* IDA contains a tree */
	IDA_BUG_ON(ida, ida_alloc_min(ida, (1 << 20) - 1, GFP_KERNEL) != (1 << 20) - 1);
	ida_free(ida, 0);
	for (i = 0; i < 31; i++)
		ida_free(ida, 1 << i);
	printk("^^^ \"not allocated\" warnings over\n");

	ida_free(ida, 3);
	ida_free(ida, 1023);
	ida_free(ida, (1 << 20) - 1);

	IDA_BUG_ON(ida, !ida_is_empty(ida));
}

	ida_check_bad_free(&ida);