[PATCH 4/8] slub_kunit: Add a test for slab_freelist_random=

From: Chris Down

Date: Tue Oct 27 2026 - 03:41:28 EST


Signed-off-by: Chris Down <chris@xxxxxxxxxxxxxx>
---
lib/tests/slub_kunit.c | 51 ++++++++++++++++++++++++++++++++++++++++++
mm/slab.h | 3 +++
mm/slub.c | 3 ++-
3 files changed, 56 insertions(+), 1 deletion(-)

diff --git a/lib/tests/slub_kunit.c b/lib/tests/slub_kunit.c
index 5ace73368523..49b9942bc564 100644
--- a/lib/tests/slub_kunit.c
+++ b/lib/tests/slub_kunit.c
@@ -190,6 +190,56 @@ static void test_clobber_redzone_free(struct kunit *test)
kmem_cache_destroy(s);
}

+static void test_freelist_random(struct kunit *test)
+{
+ struct kmem_cache *s = test_kmem_cache_create("TestSlub_freelist_random",
+ 64, SLAB_CONSISTENCY_CHECKS);
+ bool random = false;
+ unsigned int i, nr, nr_linear = 0;
+ void **objs, *first;
+
+#ifdef CONFIG_SLAB_FREELIST_RANDOM
+ random = slab_freelist_random;
+ KUNIT_EXPECT_EQ(test, !!s->random_seq, random);
+#endif
+ kunit_info(test, "freelist randomisation is %s\n", str_on_off(random));
+
+ /*
+ * Caches with debug flags have no sheaves, so objects come straight
+ * off the slab freelist.
+ */
+ first = kmem_cache_alloc(s, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, first);
+ nr = virt_to_slab(first)->objects;
+ objs = kunit_kcalloc(test, nr, sizeof(*objs), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, objs);
+ objs[0] = first;
+
+ for (i = 1; i < nr; i++) {
+ objs[i] = kmem_cache_alloc(s, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, objs[i]);
+ }
+
+ for (i = 1; i < nr; i++) {
+ if (virt_to_slab(objs[i]) != virt_to_slab(objs[0]))
+ break;
+ if (objs[i] == objs[i - 1] + s->size)
+ nr_linear++;
+ }
+
+ if (i == nr) {
+ /* Without randomisation, objects are handed out in address order. */
+ KUNIT_EXPECT_EQ(test, nr_linear == nr - 1, !random);
+ } else {
+ kunit_info(test, "objects were allocated from different slabs\n");
+ }
+
+ for (i = 0; i < nr; i++)
+ kmem_cache_free(s, objs[i]);
+ kmem_cache_destroy(s);
+ KUNIT_EXPECT_EQ(test, 0, slab_errors);
+}
+
static void test_kmalloc_redzone_access(struct kunit *test)
{
struct kmem_cache *s = test_kmem_cache_create("TestSlub_RZ_kmalloc", 32,
@@ -535,6 +585,7 @@ static struct kunit_case test_cases[] = {
#endif

KUNIT_CASE(test_clobber_redzone_free),
+ KUNIT_CASE(test_freelist_random),
KUNIT_CASE(test_kmalloc_redzone_access),
KUNIT_CASE(test_kfree_rcu),
KUNIT_CASE(test_kfree_rcu_wq_destroy),
diff --git a/mm/slab.h b/mm/slab.h
index 8187ddb2face..b648b33f25c3 100644
--- a/mm/slab.h
+++ b/mm/slab.h
@@ -887,6 +887,9 @@ static inline void dump_unreclaimable_slab(void)
void ___cache_free(struct kmem_cache *cache, void *x, unsigned long addr);

#ifdef CONFIG_SLAB_FREELIST_RANDOM
+#if IS_ENABLED(CONFIG_KUNIT)
+extern bool slab_freelist_random;
+#endif
int cache_random_seq_create(struct kmem_cache *cachep, unsigned int count,
gfp_t gfp);
void cache_random_seq_destroy(struct kmem_cache *cachep);
diff --git a/mm/slub.c b/mm/slub.c
index 7e9dadda3d10..8c7b46c2052f 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -3424,7 +3424,8 @@ static inline struct slab *alloc_slab_page(gfp_t flags, int node,
}

#ifdef CONFIG_SLAB_FREELIST_RANDOM
-static bool slab_freelist_random __ro_after_init = true;
+VISIBLE_IF_KUNIT bool slab_freelist_random __ro_after_init = true;
+EXPORT_SYMBOL_IF_KUNIT(slab_freelist_random);

static int __init setup_slab_freelist_random(char *str)
{
--
2.54.0