From 5aa2a02b985f36a9042b2c7fa63a15de096effb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 23 Feb 2026 22:37:43 +0100 Subject: [PATCH 1/5] mm/slab: create sysfs attribute through default_groups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver core can automatically create custom type attributes. This makes the code and error-handling shorter. Signed-off-by: Thomas Weißschuh Reviewed-by: Harry Yoo Link: https://patch.msgid.link/20260223-sysfs-const-slub-v1-1-ff86ffc26fff@weissschuh.net Signed-off-by: Vlastimil Babka (SUSE) --- mm/slub.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 862642c165ed..a48ea23b1728 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -9317,9 +9317,7 @@ static struct attribute *slab_attrs[] = { NULL }; -static const struct attribute_group slab_attr_group = { - .attrs = slab_attrs, -}; +ATTRIBUTE_GROUPS(slab); static ssize_t slab_attr_show(struct kobject *kobj, struct attribute *attr, @@ -9366,6 +9364,7 @@ static const struct sysfs_ops slab_sysfs_ops = { static const struct kobj_type slab_ktype = { .sysfs_ops = &slab_sysfs_ops, .release = kmem_cache_release, + .default_groups = slab_groups, }; static struct kset *slab_kset; @@ -9453,10 +9452,6 @@ static int sysfs_slab_add(struct kmem_cache *s) if (err) goto out; - err = sysfs_create_group(&s->kobj, &slab_attr_group); - if (err) - goto out_del_kobj; - if (!unmergeable) { /* Setup first alias */ sysfs_slab_alias(s, s->name); @@ -9465,9 +9460,6 @@ static int sysfs_slab_add(struct kmem_cache *s) if (!unmergeable) kfree(name); return err; -out_del_kobj: - kobject_del(&s->kobj); - goto out; } void sysfs_slab_unlink(struct kmem_cache *s) From 9042e77a5c29d42a56540b9402c8cc01b1c126e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 23 Feb 2026 22:37:44 +0100 Subject: [PATCH 2/5] mm/slab: constify sysfs attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These attributes are never modified, make them read-only. Signed-off-by: Thomas Weißschuh Reviewed-by: Harry Yoo Link: https://patch.msgid.link/20260223-sysfs-const-slub-v1-2-ff86ffc26fff@weissschuh.net Signed-off-by: Vlastimil Babka (SUSE) --- mm/slub.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index a48ea23b1728..73051cf77353 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -8833,7 +8833,7 @@ static ssize_t show_slab_objects(struct kmem_cache *s, return len; } -#define to_slab_attr(n) container_of(n, struct slab_attribute, attr) +#define to_slab_attr(n) container_of_const(n, struct slab_attribute, attr) #define to_slab(n) container_of(n, struct kmem_cache, kobj) struct slab_attribute { @@ -8843,10 +8843,10 @@ struct slab_attribute { }; #define SLAB_ATTR_RO(_name) \ - static struct slab_attribute _name##_attr = __ATTR_RO_MODE(_name, 0400) + static const struct slab_attribute _name##_attr = __ATTR_RO_MODE(_name, 0400) #define SLAB_ATTR(_name) \ - static struct slab_attribute _name##_attr = __ATTR_RW_MODE(_name, 0600) + static const struct slab_attribute _name##_attr = __ATTR_RW_MODE(_name, 0600) static ssize_t slab_size_show(struct kmem_cache *s, char *buf) { @@ -9240,7 +9240,7 @@ static ssize_t skip_kfence_store(struct kmem_cache *s, SLAB_ATTR(skip_kfence); #endif -static struct attribute *slab_attrs[] = { +static const struct attribute *const slab_attrs[] = { &slab_size_attr.attr, &object_size_attr.attr, &objs_per_slab_attr.attr, @@ -9323,7 +9323,7 @@ static ssize_t slab_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { - struct slab_attribute *attribute; + const struct slab_attribute *attribute; struct kmem_cache *s; attribute = to_slab_attr(attr); @@ -9339,7 +9339,7 @@ static ssize_t slab_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t len) { - struct slab_attribute *attribute; + const struct slab_attribute *attribute; struct kmem_cache *s; attribute = to_slab_attr(attr); From 17a9399a61c9ce89771de588f6df43a8ec91f535 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Tue, 24 Mar 2026 22:35:12 +0100 Subject: [PATCH 3/5] slab,rcu: disable KVFREE_RCU_BATCHED for strict grace period Disable CONFIG_KVFREE_RCU_BATCHED in CONFIG_RCU_STRICT_GRACE_PERIOD builds so that kernel fuzzers have an easier time finding use-after-free involving kfree_rcu(). The intent behind CONFIG_RCU_STRICT_GRACE_PERIOD is that RCU should invoke callbacks and free objects as soon as possible (at a large performance cost) so that kernel fuzzers and such have an easier time detecting use-after-free bugs in objects with RCU lifetime. CONFIG_KVFREE_RCU_BATCHED is a performance optimization that queues RCU-freed objects in ways that CONFIG_RCU_STRICT_GRACE_PERIOD can't expedite; for example, the following testcase doesn't trigger a KASAN splat when CONFIG_KVFREE_RCU_BATCHED is enabled: ``` struct foo_struct { struct rcu_head rcu; int a; }; struct foo_struct *foo = kmalloc(sizeof(*foo), GFP_KERNEL | __GFP_NOFAIL | __GFP_ZERO); pr_info("%s: calling kfree_rcu()\n", __func__); kfree_rcu(foo, rcu); msleep(10); pr_info("%s: start UAF access\n", __func__); READ_ONCE(foo->a); pr_info("%s: end UAF access\n", __func__); ``` Signed-off-by: Jann Horn Acked-by: David Rientjes Reviewed-by: Joel Fernandes Acked-by: Harry Yoo (Oracle) Link: https://patch.msgid.link/20260324-kasan-kfree-rcu-v1-1-ac58a7a13d03@google.com Signed-off-by: Vlastimil Babka (SUSE) --- mm/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/mm/Kconfig b/mm/Kconfig index ebd8ea353687..67a72fe89186 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -172,6 +172,7 @@ config SLUB config KVFREE_RCU_BATCHED def_bool y depends on !SLUB_TINY && !TINY_RCU + depends on !RCU_STRICT_GRACE_PERIOD config SLUB_TINY bool "Configure for minimal memory footprint" From 7711207dcb9b7b74270ea0fb21daf91e4291f21d Mon Sep 17 00:00:00 2001 From: "Harry Yoo (Oracle)" Date: Mon, 6 Apr 2026 18:09:06 +0900 Subject: [PATCH 4/5] MAINTAINERS: add lib/tests/slub_kunit.c to SLAB ALLOCATOR section The slub_kunit module has been maintained by SLAB ALLOCATOR folks, but is missing in the MAINTAINERS file. Add the missing entry. Acked-by: David Rientjes Signed-off-by: Harry Yoo (Oracle) Acked-by: SeongJae Park Link: https://patch.msgid.link/20260406090907.11710-2-harry@kernel.org Signed-off-by: Vlastimil Babka (SUSE) --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 55af015174a5..5418ad867e34 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -24363,6 +24363,7 @@ F: Documentation/admin-guide/mm/slab.rst F: Documentation/mm/slab.rst F: include/linux/mempool.h F: include/linux/slab.h +F: lib/tests/slub_kunit.c F: mm/failslab.c F: mm/mempool.c F: mm/slab.h From 92af129b4085cd561b59bfa1596653844cb82e4c Mon Sep 17 00:00:00 2001 From: "Harry Yoo (Oracle)" Date: Mon, 6 Apr 2026 18:09:07 +0900 Subject: [PATCH 5/5] lib/tests/slub_kunit: add a test case for {kmalloc,kfree}_nolock Testing invocation of {kmalloc,kfree}_nolock() during kmalloc() or kfree() is tricky, and it is even harder to ensure that slowpaths are properly tested. Lack of such testing has led to late discovery of the bug fixed by commit a1e244a9f177 ("mm/slab: use prandom if !allow_spin"). Add a slub_kunit test that allocates and frees objects in a tight loop while a perf event triggers interrupts (NMI or hardirq depending on the arch) on the same task, invoking {kmalloc,kfree}_nolock() from the overflow handler. Acked-by: David Rientjes Signed-off-by: Harry Yoo (Oracle) Link: https://patch.msgid.link/20260406090907.11710-3-harry@kernel.org Signed-off-by: Vlastimil Babka (SUSE) --- lib/tests/slub_kunit.c | 92 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/lib/tests/slub_kunit.c b/lib/tests/slub_kunit.c index 848b682a2d70..fa6d31dbca16 100644 --- a/lib/tests/slub_kunit.c +++ b/lib/tests/slub_kunit.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "../mm/slab.h" static struct kunit_resource resource; @@ -291,6 +292,94 @@ static void test_krealloc_redzone_zeroing(struct kunit *test) kmem_cache_destroy(s); } +#ifdef CONFIG_PERF_EVENTS +#define NR_ITERATIONS 1000 +#define NR_OBJECTS 1000 +static void *objects[NR_OBJECTS]; + +struct test_nolock_context { + struct kunit *test; + int callback_count; + int alloc_ok; + int alloc_fail; + struct perf_event *event; +}; + +static struct perf_event_attr hw_attr = { + .type = PERF_TYPE_HARDWARE, + .config = PERF_COUNT_HW_CPU_CYCLES, + .size = sizeof(struct perf_event_attr), + .pinned = 1, + .disabled = 1, + .freq = 1, + .sample_freq = 100000, +}; + +static void overflow_handler_test_kmalloc_kfree_nolock(struct perf_event *event, + struct perf_sample_data *data, + struct pt_regs *regs) +{ + void *objp; + gfp_t gfp; + struct test_nolock_context *ctx = event->overflow_handler_context; + + /* __GFP_ACCOUNT to test kmalloc_nolock() in alloc_slab_obj_exts() */ + gfp = (ctx->callback_count % 2) ? 0 : __GFP_ACCOUNT; + objp = kmalloc_nolock(64, gfp, NUMA_NO_NODE); + + if (objp) + ctx->alloc_ok++; + else + ctx->alloc_fail++; + + kfree_nolock(objp); + ctx->callback_count++; +} + +static void test_kmalloc_kfree_nolock(struct kunit *test) +{ + int i, j; + struct test_nolock_context ctx = { .test = test }; + struct perf_event *event; + bool alloc_fail = false; + + event = perf_event_create_kernel_counter(&hw_attr, -1, current, + overflow_handler_test_kmalloc_kfree_nolock, + &ctx); + if (IS_ERR(event)) + kunit_skip(test, "Failed to create perf event"); + ctx.event = event; + perf_event_enable(ctx.event); + for (i = 0; i < NR_ITERATIONS; i++) { + for (j = 0; j < NR_OBJECTS; j++) { + gfp_t gfp = (i % 2) ? GFP_KERNEL : GFP_KERNEL_ACCOUNT; + + objects[j] = kmalloc(64, gfp); + if (!objects[j]) { + j--; + while (j >= 0) + kfree(objects[j--]); + alloc_fail = true; + goto cleanup; + } + } + for (j = 0; j < NR_OBJECTS; j++) + kfree(objects[j]); + } + +cleanup: + perf_event_disable(ctx.event); + perf_event_release_kernel(ctx.event); + + kunit_info(test, "callback_count: %d, alloc_ok: %d, alloc_fail: %d\n", + ctx.callback_count, ctx.alloc_ok, ctx.alloc_fail); + + if (alloc_fail) + kunit_skip(test, "Allocation failed"); + KUNIT_EXPECT_EQ(test, 0, slab_errors); +} +#endif + static int test_init(struct kunit *test) { slab_errors = 0; @@ -315,6 +404,9 @@ static struct kunit_case test_cases[] = { KUNIT_CASE(test_kfree_rcu_wq_destroy), KUNIT_CASE(test_leak_destroy), KUNIT_CASE(test_krealloc_redzone_zeroing), +#ifdef CONFIG_PERF_EVENTS + KUNIT_CASE_SLOW(test_kmalloc_kfree_nolock), +#endif {} };