ida: Fix crash in ida_free when the bitmap is empty
authorMatthew Wilcox (Oracle) <willy@infradead.org>
Thu, 21 Dec 2023 16:53:57 +0000 (16:53 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 20 Jan 2024 10:51:46 +0000 (11:51 +0100)
[ Upstream commit af73483f4e8b6f5c68c9aa63257bdd929a9c194a ]

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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
lib/idr.c
lib/test_ida.c

index 13f2758..da36054 100644 (file)
--- a/lib/idr.c
+++ b/lib/idr.c
@@ -508,7 +508,7 @@ void ida_free(struct ida *ida, unsigned int id)
                        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);
index b068806..55105ba 100644 (file)
@@ -150,6 +150,45 @@ static void ida_check_conv(struct ida *ida)
        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)
@@ -162,6 +201,7 @@ static int ida_checks(void)
        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;