* [PATCH 0/8] mm/slab: Support selecting slab hardening features at boot
@ 2026-10-27 7:35 Chris Down
2026-10-27 7:35 ` [PATCH 1/8] mm/slub: Support disabling freelist hardening " Chris Down
` (9 more replies)
0 siblings, 10 replies; 15+ messages in thread
From: Chris Down @ 2026-10-27 7:35 UTC (permalink / raw)
To: Vlastimil Babka, Harry Yoo, Andrew Morton
Cc: Hao Li, Christoph Lameter, David Rientjes, Roman Gushchin,
Kees Cook, Marco Elver, GONG Ruiqi, Jan Kiszka, Kieran Bingham,
Jonathan Corbet, linux-mm, linux-hardening, linux-doc,
linux-kernel, kernel-team
Slab hardening settings are currently all build time choices. This means
that, unfortunately, hosts with different requirements end up needing
separate kernel images. Let's allow those hosts to use one image and
select each policy at boot instead.
This series adds four new boot parameters that default to their values
from CONFIG_*:
slab_freelist_hardened=
slab_freelist_random=
slab_buckets=
kmalloc_partition=
These mostly act at cache initialisation time. For free list hardening we
use a static key settled before the first free pointer is written.
Chris Down (8):
mm/slub: Support disabling freelist hardening at boot
slub_kunit: Add a test for slab_freelist_hardened=
mm/slub: Support disabling freelist randomisation at boot
slub_kunit: Add a test for slab_freelist_random=
mm/slab: Support disabling SLAB_BUCKETS at boot
slub_kunit: Add a test for the slab_buckets boot parameter
mm/slab: Support disabling KMALLOC_PARTITION_CACHES at boot
slub_kunit: Add a test for the kmalloc_partition boot parameter
.../admin-guide/kernel-parameters.txt | 29 +++
lib/tests/slub_kunit.c | 186 ++++++++++++++++++
mm/Kconfig | 15 ++
mm/slab.h | 24 +++
mm/slab_common.c | 48 ++++-
mm/slub.c | 68 ++++++-
scripts/gdb/linux/slab.py | 8 +-
7 files changed, 361 insertions(+), 17 deletions(-)
base-commit: d15d7703166daf7bd62c844a3cb1b3a6279adb4c
--
2.54.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 1/8] mm/slub: Support disabling freelist hardening at boot
2026-10-27 7:35 [PATCH 0/8] mm/slab: Support selecting slab hardening features at boot Chris Down
@ 2026-10-27 7:35 ` Chris Down
2026-10-27 7:35 ` [PATCH 2/8] slub_kunit: Add a test for slab_freelist_hardened= Chris Down
` (8 subsequent siblings)
9 siblings, 0 replies; 15+ messages in thread
From: Chris Down @ 2026-10-27 7:35 UTC (permalink / raw)
To: Vlastimil Babka, Harry Yoo, Andrew Morton
Cc: Hao Li, Christoph Lameter, David Rientjes, Roman Gushchin,
Kees Cook, Marco Elver, GONG Ruiqi, Jan Kiszka, Kieran Bingham,
Jonathan Corbet, linux-mm, linux-hardening, linux-doc,
linux-kernel, kernel-team
Add the slab_freelist_hardened= early parameter, enabled by default on
kernels built with CONFIG_SLAB_FREELIST_HARDENED=y.
Signed-off-by: Chris Down <chris@chrisdown.name>
---
.../admin-guide/kernel-parameters.txt | 7 +++
mm/Kconfig | 3 ++
mm/slab.h | 13 +++++
mm/slub.c | 49 +++++++++++++++----
scripts/gdb/linux/slab.py | 8 ++-
5 files changed, 69 insertions(+), 11 deletions(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 68647ff4bdd2..ddf2f6fd60be 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -7014,6 +7014,13 @@ Kernel parameters
option which can be undone by adding the
"hash_pointers=always" option.
+ slab_freelist_hardened= [MM,EARLY]
+ Format: <bool>
+ Enable or disable slab freelist hardening on kernels
+ built with CONFIG_SLAB_FREELIST_HARDENED=y. Ignored
+ without that option.
+ Default: on.
+
slab_max_order= [MM]
Determines the maximum allowed order for slabs.
A high setting may cause OOMs due to memory
diff --git a/mm/Kconfig b/mm/Kconfig
index 604c58199acb..3ae97fec9946 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -214,6 +214,9 @@ config SLAB_FREELIST_HARDENED
sacrifices to harden the kernel slab allocator against common
freelist exploit methods.
+ The hardening is enabled by default and can be disabled at boot
+ with "slab_freelist_hardened=0".
+
config SLAB_BUCKETS
bool "Support allocation from separate kmalloc buckets"
depends on !SLUB_TINY
diff --git a/mm/slab.h b/mm/slab.h
index e94ac63d10dc..8187ddb2face 100644
--- a/mm/slab.h
+++ b/mm/slab.h
@@ -540,6 +540,19 @@ static inline bool kmem_cache_debug_flags(struct kmem_cache *s, slab_flags_t fla
return false;
}
+#ifdef CONFIG_SLAB_FREELIST_HARDENED
+DECLARE_STATIC_KEY_TRUE(slab_freelist_hardened);
+#endif
+
+static inline bool slab_freelist_hardened_enabled(void)
+{
+#ifdef CONFIG_SLAB_FREELIST_HARDENED
+ return static_branch_likely(&slab_freelist_hardened);
+#else
+ return false;
+#endif
+}
+
#if IS_ENABLED(CONFIG_SLUB_DEBUG) && IS_ENABLED(CONFIG_KUNIT)
bool slab_in_kunit_test(void);
#else
diff --git a/mm/slub.c b/mm/slub.c
index 8fbc6bf8ebc5..c82bc626b657 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -214,6 +214,32 @@ DEFINE_STATIC_KEY_FALSE(slub_debug_enabled);
static DEFINE_STATIC_KEY_FALSE(strict_numa);
#endif
+#ifdef CONFIG_SLAB_FREELIST_HARDENED
+/*
+ * Free pointers stored inside free objects are encoded according to this key,
+ * so it must not change once the first slab has been populated.
+ */
+DEFINE_STATIC_KEY_TRUE_RO(slab_freelist_hardened);
+static bool slab_freelist_hardened_early __initdata = true;
+
+static int __init setup_slab_freelist_hardened(char *str)
+{
+ return kstrtobool(str, &slab_freelist_hardened_early);
+}
+early_param("slab_freelist_hardened", setup_slab_freelist_hardened);
+
+static void __init init_freelist_hardening(void)
+{
+ if (slab_freelist_hardened_early)
+ return;
+
+ static_branch_disable(&slab_freelist_hardened);
+ pr_info("SLUB: freelist hardening disabled\n");
+}
+#else
+static inline void init_freelist_hardening(void) { }
+#endif
+
#ifdef CONFIG_MEM_ALLOC_PROFILING
DEFINE_STATIC_KEY_MAYBE(CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT,
slab_obj_ext_has_codetag_key);
@@ -565,10 +591,10 @@ static inline freeptr_t freelist_ptr_encode(const struct kmem_cache *s,
{
unsigned long encoded;
-#ifdef CONFIG_SLAB_FREELIST_HARDENED
- encoded = (unsigned long)ptr ^ s->random ^ swab(ptr_addr);
-#else
encoded = (unsigned long)ptr;
+#ifdef CONFIG_SLAB_FREELIST_HARDENED
+ if (slab_freelist_hardened_enabled())
+ encoded ^= s->random ^ swab(ptr_addr);
#endif
return (freeptr_t){.v = encoded};
}
@@ -578,10 +604,10 @@ static inline void *freelist_ptr_decode(const struct kmem_cache *s,
{
void *decoded;
-#ifdef CONFIG_SLAB_FREELIST_HARDENED
- decoded = (void *)(ptr.v ^ s->random ^ swab(ptr_addr));
-#else
decoded = (void *)ptr.v;
+#ifdef CONFIG_SLAB_FREELIST_HARDENED
+ if (slab_freelist_hardened_enabled())
+ decoded = (void *)(ptr.v ^ s->random ^ swab(ptr_addr));
#endif
return decoded;
}
@@ -601,9 +627,9 @@ static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp)
{
unsigned long freeptr_addr = (unsigned long)object + s->offset;
-#ifdef CONFIG_SLAB_FREELIST_HARDENED
- BUG_ON(object == fp); /* naive detection of double free or corruption */
-#endif
+ /* naive detection of double free or corruption */
+ if (slab_freelist_hardened_enabled())
+ BUG_ON(object == fp);
freeptr_addr = (unsigned long)kasan_reset_tag((void *)freeptr_addr);
*(freeptr_t *)freeptr_addr = freelist_ptr_encode(s, fp, freeptr_addr);
@@ -6730,7 +6756,7 @@ void kmem_cache_free(struct kmem_cache *s, void *x)
slab = virt_to_slab(x);
- if (IS_ENABLED(CONFIG_SLAB_FREELIST_HARDENED) ||
+ if (slab_freelist_hardened_enabled() ||
kmem_cache_debug_flags(s, SLAB_CONSISTENCY_CHECKS)) {
/*
@@ -8766,6 +8792,9 @@ void __init kmem_cache_init(void)
if (debug_guardpage_minorder())
slub_max_order = 0;
+ /* Must be settled before any free pointer is written. */
+ init_freelist_hardening();
+
/* Inform pointer hashing choice about slub debugging state. */
hash_pointers_finalize(__slub_debug_enabled());
diff --git a/scripts/gdb/linux/slab.py b/scripts/gdb/linux/slab.py
index ddde25aeca8d..eab6cf44b54b 100644
--- a/scripts/gdb/linux/slab.py
+++ b/scripts/gdb/linux/slab.py
@@ -96,8 +96,14 @@ def swab64(x):
((x & 0xff00000000000000) >> 56))
return result
+def freelist_hardened():
+ if not constants.LX_CONFIG_SLAB_FREELIST_HARDENED:
+ return False
+ key = gdb.parse_and_eval("slab_freelist_hardened")
+ return int(key['key']['enabled']['counter']) > 0
+
def freelist_ptr_decode(cache, ptr, ptr_addr):
- if constants.LX_CONFIG_SLAB_FREELIST_HARDENED:
+ if freelist_hardened():
return ptr['v'] ^ cache['random'] ^ swab64(int(ptr_addr))
else:
return ptr['v']
--
2.54.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 2/8] slub_kunit: Add a test for slab_freelist_hardened=
2026-10-27 7:35 [PATCH 0/8] mm/slab: Support selecting slab hardening features at boot Chris Down
2026-10-27 7:35 ` [PATCH 1/8] mm/slub: Support disabling freelist hardening " Chris Down
@ 2026-10-27 7:35 ` Chris Down
2026-10-27 7:35 ` [PATCH 3/8] mm/slub: Support disabling freelist randomisation at boot Chris Down
` (7 subsequent siblings)
9 siblings, 0 replies; 15+ messages in thread
From: Chris Down @ 2026-10-27 7:35 UTC (permalink / raw)
To: Vlastimil Babka, Harry Yoo, Andrew Morton
Cc: Hao Li, Christoph Lameter, David Rientjes, Roman Gushchin,
Kees Cook, Marco Elver, GONG Ruiqi, Jan Kiszka, Kieran Bingham,
Jonathan Corbet, linux-mm, linux-hardening, linux-doc,
linux-kernel, kernel-team
Signed-off-by: Chris Down <chris@chrisdown.name>
---
lib/tests/slub_kunit.c | 52 ++++++++++++++++++++++++++++++++++++++++++
mm/slub.c | 2 ++
2 files changed, 54 insertions(+)
diff --git a/lib/tests/slub_kunit.c b/lib/tests/slub_kunit.c
index e3b63f0338d5..5ace73368523 100644
--- a/lib/tests/slub_kunit.c
+++ b/lib/tests/slub_kunit.c
@@ -9,6 +9,8 @@
#include <linux/delay.h>
#include <linux/perf_event.h>
#include <linux/kprobes.h>
+#include <linux/string_choices.h>
+#include <linux/swab.h>
#include "../mm/slab.h"
static struct kunit_resource resource;
@@ -107,6 +109,54 @@ static void test_first_word(struct kunit *test)
kmem_cache_destroy(s);
}
+static void test_freelist_hardened(struct kunit *test)
+{
+ struct kmem_cache *s = test_kmem_cache_create("TestSlub_freelist_hardened",
+ 64, SLAB_CONSISTENCY_CHECKS);
+ bool hardened = slab_freelist_hardened_enabled();
+ unsigned long ptr_addr, raw;
+ void *a, *b, *c;
+
+ a = kmem_cache_alloc(s, GFP_KERNEL);
+ b = kmem_cache_alloc(s, GFP_KERNEL);
+ c = kmem_cache_alloc(s, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, a);
+ KUNIT_ASSERT_NOT_NULL(test, b);
+ KUNIT_ASSERT_NOT_NULL(test, c);
+
+ if (virt_to_slab(a) != virt_to_slab(b) ||
+ virt_to_slab(b) != virt_to_slab(c)) {
+ kmem_cache_free(s, a);
+ kmem_cache_free(s, b);
+ kmem_cache_free(s, c);
+ kmem_cache_destroy(s);
+ kunit_skip(test, "objects were allocated from different slabs");
+ }
+
+ /*
+ * Caches with debug flags have no sheaves, so a and b are pushed onto
+ * the slab freelist in that order.
+ */
+ kmem_cache_free(s, a);
+ kmem_cache_free(s, b);
+
+ ptr_addr = (unsigned long)b + s->offset;
+ raw = *(unsigned long *)ptr_addr;
+
+ kunit_info(test, "freelist hardening is %s\n", str_on_off(hardened));
+ KUNIT_EXPECT_EQ(test, raw == (unsigned long)a, !hardened);
+#ifdef CONFIG_SLAB_FREELIST_HARDENED
+ if (hardened)
+ KUNIT_EXPECT_EQ(test, raw,
+ (unsigned long)a ^ s->random ^ swab(ptr_addr));
+#endif
+
+ kmem_cache_free(s, c);
+ validate_slab_cache(s);
+ KUNIT_EXPECT_EQ(test, 0, slab_errors);
+ kmem_cache_destroy(s);
+}
+
static void test_clobber_50th_byte(struct kunit *test)
{
struct kmem_cache *s = test_kmem_cache_create("TestSlub_50th_word_free",
@@ -480,6 +530,7 @@ static struct kunit_case test_cases[] = {
#ifndef CONFIG_KASAN
KUNIT_CASE(test_next_pointer),
KUNIT_CASE(test_first_word),
+ KUNIT_CASE(test_freelist_hardened),
KUNIT_CASE(test_clobber_50th_byte),
#endif
@@ -505,5 +556,6 @@ static struct kunit_suite test_suite = {
};
kunit_test_suite(test_suite);
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
MODULE_DESCRIPTION("Kunit tests for slub allocator");
MODULE_LICENSE("GPL");
diff --git a/mm/slub.c b/mm/slub.c
index c82bc626b657..de4937fcb293 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -46,6 +46,7 @@
#include <linux/prandom.h>
#include <kunit/test.h>
#include <kunit/test-bug.h>
+#include <kunit/visibility.h>
#include <linux/sort.h>
#include <linux/irq_work.h>
#include <linux/kprobes.h>
@@ -220,6 +221,7 @@ static DEFINE_STATIC_KEY_FALSE(strict_numa);
* so it must not change once the first slab has been populated.
*/
DEFINE_STATIC_KEY_TRUE_RO(slab_freelist_hardened);
+EXPORT_SYMBOL_IF_KUNIT(slab_freelist_hardened);
static bool slab_freelist_hardened_early __initdata = true;
static int __init setup_slab_freelist_hardened(char *str)
--
2.54.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 3/8] mm/slub: Support disabling freelist randomisation at boot
2026-10-27 7:35 [PATCH 0/8] mm/slab: Support selecting slab hardening features at boot Chris Down
2026-10-27 7:35 ` [PATCH 1/8] mm/slub: Support disabling freelist hardening " Chris Down
2026-10-27 7:35 ` [PATCH 2/8] slub_kunit: Add a test for slab_freelist_hardened= Chris Down
@ 2026-10-27 7:35 ` Chris Down
2026-10-27 7:35 ` [PATCH 4/8] slub_kunit: Add a test for slab_freelist_random= Chris Down
` (6 subsequent siblings)
9 siblings, 0 replies; 15+ messages in thread
From: Chris Down @ 2026-10-27 7:35 UTC (permalink / raw)
To: Vlastimil Babka, Harry Yoo, Andrew Morton
Cc: Hao Li, Christoph Lameter, David Rientjes, Roman Gushchin,
Kees Cook, Marco Elver, GONG Ruiqi, Jan Kiszka, Kieran Bingham,
Jonathan Corbet, linux-mm, linux-hardening, linux-doc,
linux-kernel, kernel-team
Add a slab_freelist_random= early parameter, enabled by default on
kernels built with CONFIG_SLAB_FREELIST_RANDOM=y.
Signed-off-by: Chris Down <chris@chrisdown.name>
---
Documentation/admin-guide/kernel-parameters.txt | 7 +++++++
mm/Kconfig | 3 +++
mm/slub.c | 16 ++++++++++++++++
3 files changed, 26 insertions(+)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index ddf2f6fd60be..5c4d784e7786 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -7021,6 +7021,13 @@ Kernel parameters
without that option.
Default: on.
+ slab_freelist_random= [MM,EARLY]
+ Format: <bool>
+ Enable or disable slab freelist randomisation on
+ kernels built with CONFIG_SLAB_FREELIST_RANDOM=y.
+ Ignored without that option.
+ Default: on.
+
slab_max_order= [MM]
Determines the maximum allowed order for slabs.
A high setting may cause OOMs due to memory
diff --git a/mm/Kconfig b/mm/Kconfig
index 3ae97fec9946..56c422684b44 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -205,6 +205,9 @@ config SLAB_FREELIST_RANDOM
security feature reduces the predictability of the kernel slab
allocator against heap overflows.
+ The randomisation is enabled by default and can be disabled at
+ boot with "slab_freelist_random=0".
+
config SLAB_FREELIST_HARDENED
bool "Harden slab freelist metadata"
depends on !SLUB_TINY
diff --git a/mm/slub.c b/mm/slub.c
index de4937fcb293..7e9dadda3d10 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -3424,12 +3424,23 @@ 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;
+
+static int __init setup_slab_freelist_random(char *str)
+{
+ return kstrtobool(str, &slab_freelist_random);
+}
+early_param("slab_freelist_random", setup_slab_freelist_random);
+
/* Pre-initialize the random sequence cache */
static int init_cache_random_seq(struct kmem_cache *s)
{
unsigned int count = oo_objects(s->oo);
int err;
+ if (!slab_freelist_random)
+ return 0;
+
/* Bailout if already initialised */
if (s->random_seq)
return 0;
@@ -3456,6 +3467,11 @@ static void __init init_freelist_randomization(void)
{
struct kmem_cache *s;
+ if (!slab_freelist_random) {
+ pr_info("SLUB: freelist randomisation disabled\n");
+ return;
+ }
+
mutex_lock(&slab_mutex);
list_for_each_entry(s, &slab_caches, list)
--
2.54.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 4/8] slub_kunit: Add a test for slab_freelist_random=
2026-10-27 7:35 [PATCH 0/8] mm/slab: Support selecting slab hardening features at boot Chris Down
` (2 preceding siblings ...)
2026-10-27 7:35 ` [PATCH 3/8] mm/slub: Support disabling freelist randomisation at boot Chris Down
@ 2026-10-27 7:35 ` Chris Down
2026-10-27 7:35 ` [PATCH 5/8] mm/slab: Support disabling SLAB_BUCKETS at boot Chris Down
` (5 subsequent siblings)
9 siblings, 0 replies; 15+ messages in thread
From: Chris Down @ 2026-10-27 7:35 UTC (permalink / raw)
To: Vlastimil Babka, Harry Yoo, Andrew Morton
Cc: Hao Li, Christoph Lameter, David Rientjes, Roman Gushchin,
Kees Cook, Marco Elver, GONG Ruiqi, Jan Kiszka, Kieran Bingham,
Jonathan Corbet, linux-mm, linux-hardening, linux-doc,
linux-kernel, kernel-team
Signed-off-by: Chris Down <chris@chrisdown.name>
---
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
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 5/8] mm/slab: Support disabling SLAB_BUCKETS at boot
2026-10-27 7:35 [PATCH 0/8] mm/slab: Support selecting slab hardening features at boot Chris Down
` (3 preceding siblings ...)
2026-10-27 7:35 ` [PATCH 4/8] slub_kunit: Add a test for slab_freelist_random= Chris Down
@ 2026-10-27 7:35 ` Chris Down
2026-10-27 7:36 ` [PATCH 6/8] slub_kunit: Add a test for the slab_buckets boot parameter Chris Down
` (4 subsequent siblings)
9 siblings, 0 replies; 15+ messages in thread
From: Chris Down @ 2026-10-27 7:35 UTC (permalink / raw)
To: Vlastimil Babka, Harry Yoo, Andrew Morton
Cc: Hao Li, Christoph Lameter, David Rientjes, Roman Gushchin,
Kees Cook, Marco Elver, GONG Ruiqi, Jan Kiszka, Kieran Bingham,
Jonathan Corbet, linux-mm, linux-hardening, linux-doc,
linux-kernel, kernel-team
Add a slab_buckets= early parameter, enabled by default on kernels built
with CONFIG_SLAB_BUCKETS=y.
Signed-off-by: Chris Down <chris@chrisdown.name>
---
.../admin-guide/kernel-parameters.txt | 8 ++++++
mm/Kconfig | 4 +++
mm/slab_common.c | 26 +++++++++++++++----
3 files changed, 33 insertions(+), 5 deletions(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 5c4d784e7786..20710d6ccd1f 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -7001,6 +7001,14 @@ Kernel parameters
apic=verbose is specified.
Example: apic=debug show_lapic=all
+ slab_buckets= [MM,EARLY]
+ Format: <bool>
+ Enable or disable separate kmalloc buckets on kernels
+ built with CONFIG_SLAB_BUCKETS=y. When disabled,
+ allocations use regular kmalloc caches. Ignored
+ without that option.
+ Default: on.
+
slab_debug[=options[,slabs][;[options[,slabs]]...]] [MM]
Enabling slab_debug allows one to determine the
culprit if slab objects become corrupted. Enabling
diff --git a/mm/Kconfig b/mm/Kconfig
index 56c422684b44..ebdf14a87c71 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -235,6 +235,10 @@ config SLAB_BUCKETS
of extra pages since the bulk of user-controlled allocations
are relatively long-lived.
+ The separate buckets can be disabled at boot with the
+ "slab_buckets=0" kernel parameter, in which case these
+ allocations use the regular kmalloc caches.
+
If unsure, say Y.
config SLUB_STATS
diff --git a/mm/slab_common.c b/mm/slab_common.c
index e66c0778ee98..b7a46c368ab1 100644
--- a/mm/slab_common.c
+++ b/mm/slab_common.c
@@ -410,6 +410,16 @@ EXPORT_SYMBOL(__kmem_cache_create_args);
static struct kmem_cache *kmem_buckets_cache __ro_after_init;
+static bool slab_buckets_enabled __ro_after_init = IS_ENABLED(CONFIG_SLAB_BUCKETS);
+
+#ifdef CONFIG_SLAB_BUCKETS
+static int __init setup_slab_buckets(char *str)
+{
+ return kstrtobool(str, &slab_buckets_enabled);
+}
+early_param("slab_buckets", setup_slab_buckets);
+#endif
+
/**
* kmem_buckets_create - Create a set of caches that handle dynamic sized
* allocations via kmem_buckets_alloc()
@@ -424,10 +434,12 @@ static struct kmem_cache *kmem_buckets_cache __ro_after_init;
*
* Context: Cannot be called within an interrupt, but can be interrupted.
*
- * Return: a pointer to the cache on success, NULL on failure. When
- * CONFIG_SLAB_BUCKETS is not enabled, ZERO_SIZE_PTR is returned, and
- * subsequent calls to kmem_buckets_alloc() will fall back to kmalloc().
- * (i.e. callers only need to check for NULL on failure.)
+ * Return: a pointer to the cache on success, NULL on failure or when the
+ * separate buckets were disabled with the "slab_buckets=0" boot parameter.
+ * When CONFIG_SLAB_BUCKETS is not enabled, ZERO_SIZE_PTR is returned. In all
+ * of these cases subsequent calls to kmem_buckets_alloc() with the returned
+ * value fall back to kmalloc(), so callers that can live without separate
+ * buckets do not need to check the return value.
*/
kmem_buckets *kmem_buckets_create(const char *name, slab_flags_t flags,
unsigned int useroffset,
@@ -448,6 +460,10 @@ kmem_buckets *kmem_buckets_create(const char *name, slab_flags_t flags,
if (!IS_ENABLED(CONFIG_SLAB_BUCKETS))
return ZERO_SIZE_PTR;
+ /* NULL makes kmalloc_slab() use the regular kmalloc caches. */
+ if (!slab_buckets_enabled)
+ return NULL;
+
if (WARN_ON(!kmem_buckets_cache))
return NULL;
@@ -1059,7 +1075,7 @@ void __init create_kmalloc_caches(void)
/* Kmalloc array is now usable */
slab_state = UP;
- if (IS_ENABLED(CONFIG_SLAB_BUCKETS))
+ if (IS_ENABLED(CONFIG_SLAB_BUCKETS) && slab_buckets_enabled)
kmem_buckets_cache = kmem_cache_create("kmalloc_buckets",
sizeof(kmem_buckets),
0, SLAB_NO_MERGE, NULL);
--
2.54.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 6/8] slub_kunit: Add a test for the slab_buckets boot parameter
2026-10-27 7:35 [PATCH 0/8] mm/slab: Support selecting slab hardening features at boot Chris Down
` (4 preceding siblings ...)
2026-10-27 7:35 ` [PATCH 5/8] mm/slab: Support disabling SLAB_BUCKETS at boot Chris Down
@ 2026-10-27 7:36 ` Chris Down
2026-10-27 7:36 ` [PATCH 7/8] mm/slab: Support disabling KMALLOC_PARTITION_CACHES at boot Chris Down
` (3 subsequent siblings)
9 siblings, 0 replies; 15+ messages in thread
From: Chris Down @ 2026-10-27 7:36 UTC (permalink / raw)
To: Vlastimil Babka, Harry Yoo, Andrew Morton
Cc: Hao Li, Christoph Lameter, David Rientjes, Roman Gushchin,
Kees Cook, Marco Elver, GONG Ruiqi, Jan Kiszka, Kieran Bingham,
Jonathan Corbet, linux-mm, linux-hardening, linux-doc,
linux-kernel, kernel-team
Signed-off-by: Chris Down <chris@chrisdown.name>
---
lib/tests/slub_kunit.c | 37 +++++++++++++++++++++++++++++++++++++
mm/slab.h | 4 ++++
mm/slab_common.c | 4 +++-
3 files changed, 44 insertions(+), 1 deletion(-)
diff --git a/lib/tests/slub_kunit.c b/lib/tests/slub_kunit.c
index 49b9942bc564..646596663b33 100644
--- a/lib/tests/slub_kunit.c
+++ b/lib/tests/slub_kunit.c
@@ -11,6 +11,8 @@
#include <linux/kprobes.h>
#include <linux/string_choices.h>
#include <linux/swab.h>
+#include <linux/mman.h>
+#include <linux/string.h>
#include "../mm/slab.h"
static struct kunit_resource resource;
@@ -396,6 +398,38 @@ static void test_krealloc_redzone_zeroing(struct kunit *test)
kmem_cache_destroy(s);
}
+#ifdef CONFIG_SLAB_BUCKETS
+static void test_slab_buckets(struct kunit *test)
+{
+ unsigned long user_addr;
+ const char *name;
+ void *p;
+
+ if (!slab_buckets_enabled)
+ KUNIT_EXPECT_NULL(test, kmem_buckets_create("TestSlub_buckets",
+ 0, 0, INT_MAX, NULL));
+
+ user_addr = kunit_vm_mmap(test, NULL, 0, PAGE_SIZE,
+ PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, 0);
+ KUNIT_ASSERT_NE_MSG(test, user_addr, 0, "Could not create userspace mm");
+ KUNIT_ASSERT_LT_MSG(test, user_addr, (unsigned long)TASK_SIZE,
+ "Failed to allocate user memory");
+
+ p = memdup_user((void __user *)user_addr, 64);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, p);
+
+ name = virt_to_slab(p)->slab_cache->name;
+ if (slab_buckets_enabled)
+ KUNIT_EXPECT_TRUE_MSG(test, strstarts(name, "memdup_user-"),
+ "allocated from %s", name);
+ else
+ KUNIT_EXPECT_TRUE_MSG(test, strstarts(name, "kmalloc-"),
+ "allocated from %s", name);
+ kfree(p);
+}
+#endif
+
#if defined(CONFIG_PERF_EVENTS) || (defined(CONFIG_KPROBES) && defined(CONFIG_SMP))
#define NR_ITERATIONS 1000
#define NR_OBJECTS 1000
@@ -591,6 +625,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_SLAB_BUCKETS
+ KUNIT_CASE(test_slab_buckets),
+#endif
#ifdef CONFIG_PERF_EVENTS
KUNIT_CASE_SLOW(test_kmalloc_nolock_and_friends_perf),
#endif
diff --git a/mm/slab.h b/mm/slab.h
index b648b33f25c3..185df5e68419 100644
--- a/mm/slab.h
+++ b/mm/slab.h
@@ -390,6 +390,10 @@ extern struct list_head slab_caches;
/* The slab cache that manages slab cache information */
extern struct kmem_cache *kmem_cache;
+#if IS_ENABLED(CONFIG_KUNIT)
+extern bool slab_buckets_enabled;
+#endif
+
/* A table of kmalloc cache names and sizes */
extern const struct kmalloc_info_struct {
const char *name[NR_KMALLOC_TYPES];
diff --git a/mm/slab_common.c b/mm/slab_common.c
index b7a46c368ab1..9fe48da12010 100644
--- a/mm/slab_common.c
+++ b/mm/slab_common.c
@@ -29,6 +29,7 @@
#include <linux/memcontrol.h>
#include <linux/stackdepot.h>
#include <trace/events/rcu.h>
+#include <kunit/visibility.h>
#include "../kernel/rcu/rcu.h"
#include "internal.h"
@@ -410,7 +411,8 @@ EXPORT_SYMBOL(__kmem_cache_create_args);
static struct kmem_cache *kmem_buckets_cache __ro_after_init;
-static bool slab_buckets_enabled __ro_after_init = IS_ENABLED(CONFIG_SLAB_BUCKETS);
+VISIBLE_IF_KUNIT bool slab_buckets_enabled __ro_after_init = IS_ENABLED(CONFIG_SLAB_BUCKETS);
+EXPORT_SYMBOL_IF_KUNIT(slab_buckets_enabled);
#ifdef CONFIG_SLAB_BUCKETS
static int __init setup_slab_buckets(char *str)
--
2.54.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 7/8] mm/slab: Support disabling KMALLOC_PARTITION_CACHES at boot
2026-10-27 7:35 [PATCH 0/8] mm/slab: Support selecting slab hardening features at boot Chris Down
` (5 preceding siblings ...)
2026-10-27 7:36 ` [PATCH 6/8] slub_kunit: Add a test for the slab_buckets boot parameter Chris Down
@ 2026-10-27 7:36 ` Chris Down
2026-10-27 7:36 ` [PATCH 8/8] slub_kunit: Add a test for the kmalloc_partition boot parameter Chris Down
` (2 subsequent siblings)
9 siblings, 0 replies; 15+ messages in thread
From: Chris Down @ 2026-10-27 7:36 UTC (permalink / raw)
To: Vlastimil Babka, Harry Yoo, Andrew Morton
Cc: Hao Li, Christoph Lameter, David Rientjes, Roman Gushchin,
Kees Cook, Marco Elver, GONG Ruiqi, Jan Kiszka, Kieran Bingham,
Jonathan Corbet, linux-mm, linux-hardening, linux-doc,
linux-kernel, kernel-team
Add a kmalloc_partition= early parameter, enabled by default on kernels
built with CONFIG_KMALLOC_PARTITION_CACHES=y.
Let's leave kmalloc_type() unchanged since each index simply resolves to
the same cache when partitioning is off.
Signed-off-by: Chris Down <chris@chrisdown.name>
---
.../admin-guide/kernel-parameters.txt | 7 +++++++
mm/Kconfig | 5 +++++
mm/slab_common.c | 19 ++++++++++++++++++-
3 files changed, 30 insertions(+), 1 deletion(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 20710d6ccd1f..9752bd2aaa72 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -3054,6 +3054,13 @@ Kernel parameters
Configure the RouterBoard 532 series on-chip
Ethernet adapter MAC address.
+ kmalloc_partition= [MM,EARLY]
+ Format: <bool>
+ Enable or disable kmalloc cache partitioning on
+ kernels built with CONFIG_KMALLOC_PARTITION_CACHES=y.
+ Ignored without that option.
+ Default: on.
+
kmemleak= [KNL,EARLY] Boot-time kmemleak enable/disable
Valid arguments: on, off
Default: on
diff --git a/mm/Kconfig b/mm/Kconfig
index ebdf14a87c71..12fbd7075705 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -270,6 +270,11 @@ config KMALLOC_PARTITION_CACHES
limited degree of memory and CPU overhead that relates to hardware
and system workload.
+ The partitioning can be disabled at boot with the
+ "kmalloc_partition=0" kernel parameter, in which case the copies
+ are not created and all normal kmalloc allocations use the
+ regular kmalloc caches.
+
choice
prompt "Partitioned slab cache mode"
depends on KMALLOC_PARTITION_CACHES
diff --git a/mm/slab_common.c b/mm/slab_common.c
index 9fe48da12010..5391149a2eed 100644
--- a/mm/slab_common.c
+++ b/mm/slab_common.c
@@ -781,6 +781,16 @@ unsigned long random_kmalloc_seed __ro_after_init;
EXPORT_SYMBOL(random_kmalloc_seed);
#endif
+#ifdef CONFIG_KMALLOC_PARTITION_CACHES
+static bool kmalloc_partition_enabled __ro_after_init = true;
+
+static int __init setup_kmalloc_partition(char *str)
+{
+ return kstrtobool(str, &kmalloc_partition_enabled);
+}
+early_param("kmalloc_partition", setup_kmalloc_partition);
+#endif
+
/*
* Conversion table for small slabs sizes / 8 to the index in the
* kmalloc array. This is necessary for slabs < 192 since we have non power
@@ -1013,8 +1023,15 @@ new_kmalloc_cache(int idx, enum kmalloc_cache_type type)
}
#ifdef CONFIG_KMALLOC_PARTITION_CACHES
- if (type >= KMALLOC_PARTITION_START && type <= KMALLOC_PARTITION_END)
+ if (!kmalloc_partition_enabled) {
+ /* Use the normal caches, which have already been created. */
+ if (type > KMALLOC_PARTITION_START && type <= KMALLOC_PARTITION_END) {
+ kmalloc_caches[type][idx] = kmalloc_caches[KMALLOC_NORMAL][idx];
+ return;
+ }
+ } else if (type >= KMALLOC_PARTITION_START && type <= KMALLOC_PARTITION_END) {
flags |= SLAB_NO_MERGE;
+ }
#endif
/*
--
2.54.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 8/8] slub_kunit: Add a test for the kmalloc_partition boot parameter
2026-10-27 7:35 [PATCH 0/8] mm/slab: Support selecting slab hardening features at boot Chris Down
` (6 preceding siblings ...)
2026-10-27 7:36 ` [PATCH 7/8] mm/slab: Support disabling KMALLOC_PARTITION_CACHES at boot Chris Down
@ 2026-10-27 7:36 ` Chris Down
2026-10-27 8:10 ` [PATCH 0/8] mm/slab: Support selecting slab hardening features at boot Vlastimil Babka (SUSE)
2026-10-28 15:06 ` Harry Yoo
9 siblings, 0 replies; 15+ messages in thread
From: Chris Down @ 2026-10-27 7:36 UTC (permalink / raw)
To: Vlastimil Babka, Harry Yoo, Andrew Morton
Cc: Hao Li, Christoph Lameter, David Rientjes, Roman Gushchin,
Kees Cook, Marco Elver, GONG Ruiqi, Jan Kiszka, Kieran Bingham,
Jonathan Corbet, linux-mm, linux-hardening, linux-doc,
linux-kernel, kernel-team
Signed-off-by: Chris Down <chris@chrisdown.name>
---
lib/tests/slub_kunit.c | 46 ++++++++++++++++++++++++++++++++++++++++++
mm/slab.h | 4 ++++
mm/slab_common.c | 3 ++-
3 files changed, 52 insertions(+), 1 deletion(-)
diff --git a/lib/tests/slub_kunit.c b/lib/tests/slub_kunit.c
index 646596663b33..0fe10cbfc83d 100644
--- a/lib/tests/slub_kunit.c
+++ b/lib/tests/slub_kunit.c
@@ -430,6 +430,49 @@ static void test_slab_buckets(struct kunit *test)
}
#endif
+#ifdef CONFIG_KMALLOC_PARTITION_CACHES
+/*
+ * Each partition copy is a separate cache, unless the copies were disabled
+ * with kmalloc_partition=0: then they alias the KMALLOC_NORMAL caches.
+ */
+static void test_kmalloc_partition(struct kunit *test)
+{
+ enum kmalloc_cache_type type;
+ struct kmem_cache *s;
+ unsigned int idx;
+ bool found;
+ void *p;
+
+ for (type = KMALLOC_PARTITION_START + 1; type <= KMALLOC_PARTITION_END; type++) {
+ for (idx = 0; idx < ARRAY_SIZE(kmalloc_caches[type]); idx++) {
+ struct kmem_cache *normal = kmalloc_caches[KMALLOC_NORMAL][idx];
+ struct kmem_cache *part = kmalloc_caches[type][idx];
+
+ if (!kmalloc_partition_enabled) {
+ KUNIT_EXPECT_PTR_EQ(test, part, normal);
+ } else if (normal) {
+ KUNIT_EXPECT_NOT_NULL(test, part);
+ KUNIT_EXPECT_PTR_NE(test, part, normal);
+ }
+ }
+ }
+
+ p = kmalloc(64, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, p);
+ s = virt_to_slab(p)->slab_cache;
+ idx = kmalloc_index(64);
+ if (!kmalloc_partition_enabled) {
+ KUNIT_EXPECT_PTR_EQ(test, s, kmalloc_caches[KMALLOC_NORMAL][idx]);
+ } else {
+ found = false;
+ for (type = KMALLOC_PARTITION_START; type <= KMALLOC_PARTITION_END; type++)
+ found |= s == kmalloc_caches[type][idx];
+ KUNIT_EXPECT_TRUE_MSG(test, found, "allocated from %s", s->name);
+ }
+ kfree(p);
+}
+#endif
+
#if defined(CONFIG_PERF_EVENTS) || (defined(CONFIG_KPROBES) && defined(CONFIG_SMP))
#define NR_ITERATIONS 1000
#define NR_OBJECTS 1000
@@ -628,6 +671,9 @@ static struct kunit_case test_cases[] = {
#ifdef CONFIG_SLAB_BUCKETS
KUNIT_CASE(test_slab_buckets),
#endif
+#ifdef CONFIG_KMALLOC_PARTITION_CACHES
+ KUNIT_CASE(test_kmalloc_partition),
+#endif
#ifdef CONFIG_PERF_EVENTS
KUNIT_CASE_SLOW(test_kmalloc_nolock_and_friends_perf),
#endif
diff --git a/mm/slab.h b/mm/slab.h
index 185df5e68419..934b9db1ef6b 100644
--- a/mm/slab.h
+++ b/mm/slab.h
@@ -394,6 +394,10 @@ extern struct kmem_cache *kmem_cache;
extern bool slab_buckets_enabled;
#endif
+#if IS_ENABLED(CONFIG_KUNIT) && defined(CONFIG_KMALLOC_PARTITION_CACHES)
+extern bool kmalloc_partition_enabled;
+#endif
+
/* A table of kmalloc cache names and sizes */
extern const struct kmalloc_info_struct {
const char *name[NR_KMALLOC_TYPES];
diff --git a/mm/slab_common.c b/mm/slab_common.c
index 5391149a2eed..406e8688ee0e 100644
--- a/mm/slab_common.c
+++ b/mm/slab_common.c
@@ -782,7 +782,8 @@ EXPORT_SYMBOL(random_kmalloc_seed);
#endif
#ifdef CONFIG_KMALLOC_PARTITION_CACHES
-static bool kmalloc_partition_enabled __ro_after_init = true;
+VISIBLE_IF_KUNIT bool kmalloc_partition_enabled __ro_after_init = true;
+EXPORT_SYMBOL_IF_KUNIT(kmalloc_partition_enabled);
static int __init setup_kmalloc_partition(char *str)
{
--
2.54.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 0/8] mm/slab: Support selecting slab hardening features at boot
2026-10-27 7:35 [PATCH 0/8] mm/slab: Support selecting slab hardening features at boot Chris Down
` (7 preceding siblings ...)
2026-10-27 7:36 ` [PATCH 8/8] slub_kunit: Add a test for the kmalloc_partition boot parameter Chris Down
@ 2026-10-27 8:10 ` Vlastimil Babka (SUSE)
2026-10-27 18:31 ` Harry Yoo
2026-10-28 15:23 ` Chris Down
2026-10-28 15:06 ` Harry Yoo
9 siblings, 2 replies; 15+ messages in thread
From: Vlastimil Babka (SUSE) @ 2026-10-27 8:10 UTC (permalink / raw)
To: Chris Down, Harry Yoo, Andrew Morton
Cc: Hao Li, Christoph Lameter, David Rientjes, Roman Gushchin,
Kees Cook, Marco Elver, GONG Ruiqi, Jan Kiszka, Kieran Bingham,
Jonathan Corbet, linux-mm, linux-hardening, linux-doc,
linux-kernel, kernel-team
On 10/27/26 08:35, Chris Down wrote:
> Slab hardening settings are currently all build time choices. This means
> that, unfortunately, hosts with different requirements end up needing
> separate kernel images. Let's allow those hosts to use one image and
> select each policy at boot instead.
>
> This series adds four new boot parameters that default to their values
> from CONFIG_*:
>
> slab_freelist_hardened=
> slab_freelist_random=
These two should be only a small cpu overhead, which is why distros
generally enable them [1]. Also the code basically only runs in slowpaths
because percpu caching doesn't use freelists. It's possible the static key
conversion doesn't help anything. You could try to measure the impact, I'd
be surprised perhaps :)
Maybe we should just make them default=y.
> slab_buckets=
> kmalloc_partition=
These are more valid as there's a memory overhead tradeoff.
[1]
https://oracle.github.io/kconfigs/?config=UTS_RELEASE&config=SLAB_FREELIST_RANDOM&config=SLAB_FREELIST_HARDENED
> These mostly act at cache initialisation time. For free list hardening we
> use a static key settled before the first free pointer is written.
>
> Chris Down (8):
> mm/slub: Support disabling freelist hardening at boot
> slub_kunit: Add a test for slab_freelist_hardened=
> mm/slub: Support disabling freelist randomisation at boot
> slub_kunit: Add a test for slab_freelist_random=
> mm/slab: Support disabling SLAB_BUCKETS at boot
> slub_kunit: Add a test for the slab_buckets boot parameter
> mm/slab: Support disabling KMALLOC_PARTITION_CACHES at boot
> slub_kunit: Add a test for the kmalloc_partition boot parameter
>
> .../admin-guide/kernel-parameters.txt | 29 +++
> lib/tests/slub_kunit.c | 186 ++++++++++++++++++
> mm/Kconfig | 15 ++
> mm/slab.h | 24 +++
> mm/slab_common.c | 48 ++++-
> mm/slub.c | 68 ++++++-
> scripts/gdb/linux/slab.py | 8 +-
> 7 files changed, 361 insertions(+), 17 deletions(-)
>
>
> base-commit: d15d7703166daf7bd62c844a3cb1b3a6279adb4c
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 0/8] mm/slab: Support selecting slab hardening features at boot
2026-10-27 8:10 ` [PATCH 0/8] mm/slab: Support selecting slab hardening features at boot Vlastimil Babka (SUSE)
@ 2026-10-27 18:31 ` Harry Yoo
2026-10-28 15:23 ` Chris Down
1 sibling, 0 replies; 15+ messages in thread
From: Harry Yoo @ 2026-10-27 18:31 UTC (permalink / raw)
To: Vlastimil Babka (SUSE)
Cc: Chris Down, Andrew Morton, Hao Li, Christoph Lameter,
David Rientjes, Roman Gushchin, Kees Cook, Marco Elver,
GONG Ruiqi, Jan Kiszka, Kieran Bingham, Jonathan Corbet,
linux-mm, linux-hardening, linux-doc, linux-kernel, kernel-team
On Tue, Oct 27, 2026 at 08:10:40AM +0100, Vlastimil Babka (SUSE) wrote:
> These two should be only a small cpu overhead, which is why distros
> generally enable them [1].
Yeah I'd expect the impact on them to be small enough.
> > slab_buckets=
> > kmalloc_partition=
>
> These are more valid as there's a memory overhead tradeoff.
quickly measuring it on a VM with 64 CPUs and 64 GiB RAM:
w/ partitioned kmalloc: 94.0 MiB (235 kmalloc caches)
w/o partitioned kmalloc: 76.1 MiB (~17.9 MiB diff) (40 kmalloc caches)
Mostly due to a large number number of under-utilized slabs after boot.
(per memalloc profiling)
slab_buckets had relately smaller impact, about ~1MiB after boot.
(Of course this is just after boot, and actual bloat depends on memory
usage and fragmentation on production)
--
Cheers,
Harry / Hyeonggon
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 0/8] mm/slab: Support selecting slab hardening features at boot
2026-10-27 7:35 [PATCH 0/8] mm/slab: Support selecting slab hardening features at boot Chris Down
` (8 preceding siblings ...)
2026-10-27 8:10 ` [PATCH 0/8] mm/slab: Support selecting slab hardening features at boot Vlastimil Babka (SUSE)
@ 2026-10-28 15:06 ` Harry Yoo
2026-10-28 15:27 ` Chris Down
9 siblings, 1 reply; 15+ messages in thread
From: Harry Yoo @ 2026-10-28 15:06 UTC (permalink / raw)
To: Chris Down
Cc: Vlastimil Babka, Andrew Morton, Hao Li, Christoph Lameter,
David Rientjes, Roman Gushchin, Kees Cook, Marco Elver,
GONG Ruiqi, Jan Kiszka, Kieran Bingham, Jonathan Corbet,
linux-mm, linux-hardening, linux-doc, linux-kernel, kernel-team
Hi Chris!
On Tue, Oct 27, 2026 at 03:35:16PM +0800, Chris Down wrote:
> Slab hardening settings are currently all build time choices. This means
> that, unfortunately, hosts with different requirements end up needing
> separate kernel images. Let's allow those hosts to use one image and
> select each policy at boot instead.
>
> This series adds four new boot parameters that default to their values
> from CONFIG_*:
>
> slab_freelist_hardened=
> slab_freelist_random=
> slab_buckets=
> kmalloc_partition=
If we end up supporting boot-time parameters for hardening,
I think it should be all enabled (or disabled) by:
slab_hardening=none|light|full
# please feel free to suggest bertter names
none: "Turn of all of them to maximize performance"
light: "I'm willying to sacrifice _some_ memory and CPU for security"
full: "I'm willying to sacrifice more overhead for security
IMHO more fine-grained knob for each feature doesn't make much sense.
--
Cheers,
Harry / Hyeonggon
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 0/8] mm/slab: Support selecting slab hardening features at boot
2026-10-27 8:10 ` [PATCH 0/8] mm/slab: Support selecting slab hardening features at boot Vlastimil Babka (SUSE)
2026-10-27 18:31 ` Harry Yoo
@ 2026-10-28 15:23 ` Chris Down
1 sibling, 0 replies; 15+ messages in thread
From: Chris Down @ 2026-10-28 15:23 UTC (permalink / raw)
To: Vlastimil Babka (SUSE)
Cc: Harry Yoo, Andrew Morton, Hao Li, Christoph Lameter,
David Rientjes, Roman Gushchin, Kees Cook, Marco Elver,
GONG Ruiqi, Jan Kiszka, Kieran Bingham, Jonathan Corbet,
linux-mm, linux-hardening, linux-doc, linux-kernel, kernel-team
Hi Vlastimil,
Vlastimil Babka (SUSE) writes:
>These two should be only a small cpu overhead, which is why distros
>generally enable them [1]. Also the code basically only runs in slowpaths
>because percpu caching doesn't use freelists. It's possible the static key
>conversion doesn't help anything. You could try to measure the impact, I'd
>be surprised perhaps :)
Well I measured it, and you're right :-)
I built kernels from d15d7703166d ("mm/slub: initialize the free pointer of the
bootstrap kmem_cache_node") toggling SLAB_FREELIST_HARDENED and
SLAB_FREELIST_RANDOM, with SLAB_BUCKETS, KMALLOC_PARTITION_CACHES and
SHUFFLE_PAGE_ALLOCATOR pinned off on an AMD Genoa.
With both on vs both off, sched messaging, perf bench syscall fork, a
tinyconfig kernel build and netperf over lo all moved by less than 0.8%, which
is within the noise here. So the slowpaths do pay, as you'd expect. Here's the
change in ns/object vs both off, over 64, 256 and 1024 byte objects:
hard rand both
batch alloc +5.5..+7.8% +7.6..+12.5% +13..+15%
bulk alloc -2.0..+3.9% +28..+32% +33..+38%
batch/bulk free +0.5..+4.6% -1.7..+0.6% +0.8..+4.5%
These are from a small module which just does ktime_get_ns() on one pinned CPU.
batch allocates 4096 objects with kmalloc() and then frees them 1000 times.
bulk does the same with kmem_cache_alloc_bulk() and kmem_cache_free_bulk() on a
private cache, so it keeps creating new slabs, which is where the random seq
gets applied.
So for us the right answer is to likely just turn these two on rather than make
them switchable, too.
>Maybe we should just make them default=y.
Agreed. In v2 I'll replace the boot parameters for these two with patches which
make them default y, and keep slab_buckets= and kmalloc_partition=.
Thank you for spotting this!
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 0/8] mm/slab: Support selecting slab hardening features at boot
2026-10-28 15:06 ` Harry Yoo
@ 2026-10-28 15:27 ` Chris Down
2026-10-28 15:37 ` Harry Yoo
0 siblings, 1 reply; 15+ messages in thread
From: Chris Down @ 2026-10-28 15:27 UTC (permalink / raw)
To: Harry Yoo
Cc: Vlastimil Babka, Andrew Morton, Hao Li, Christoph Lameter,
David Rientjes, Roman Gushchin, Kees Cook, Marco Elver,
GONG Ruiqi, Jan Kiszka, Kieran Bingham, Jonathan Corbet,
linux-mm, linux-hardening, linux-doc, linux-kernel, kernel-team
Hi Harry,
Harry Yoo writes:
>If we end up supporting boot-time parameters for hardening,
>I think it should be all enabled (or disabled) by:
>
> slab_hardening=none|light|full
> # please feel free to suggest bertter names
>
>none: "Turn of all of them to maximize performance"
>
>light: "I'm willying to sacrifice _some_ memory and CPU for security"
>
>full: "I'm willying to sacrifice more overhead for security
>
>IMHO more fine-grained knob for each feature doesn't make much sense.
I think if we did the 4 knobs that is a good idea. Given now it's two, I think
it seems reasonable to keep it as two separate knobs unless you object?
Otherwise I think given it will only cover the latter two, it should be
something like kmalloc_isolation= instead. Let me know what you think.
Thanks!
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 0/8] mm/slab: Support selecting slab hardening features at boot
2026-10-28 15:27 ` Chris Down
@ 2026-10-28 15:37 ` Harry Yoo
0 siblings, 0 replies; 15+ messages in thread
From: Harry Yoo @ 2026-10-28 15:37 UTC (permalink / raw)
To: Chris Down
Cc: Vlastimil Babka, Andrew Morton, Hao Li, Christoph Lameter,
David Rientjes, Roman Gushchin, Kees Cook, Marco Elver,
GONG Ruiqi, Jan Kiszka, Kieran Bingham, Jonathan Corbet,
linux-mm, linux-hardening, linux-doc, linux-kernel, kernel-team
On Wed, Oct 28, 2026 at 11:27:37PM +0800, Chris Down wrote:
> Hi Harry,
>
> Harry Yoo writes:
> > If we end up supporting boot-time parameters for hardening,
> > I think it should be all enabled (or disabled) by:
> >
> > slab_hardening=none|light|full
> > # please feel free to suggest bertter names
> >
> > none: "Turn of all of them to maximize performance"
> >
> > light: "I'm willying to sacrifice _some_ memory and CPU for security"
> >
> > full: "I'm willying to sacrifice more overhead for security
> >
> > IMHO more fine-grained knob for each feature doesn't make much sense.
>
> I think if we did the 4 knobs that is a good idea. Given now it's two, I
> think it seems reasonable to keep it as two separate knobs unless you
> object? Otherwise I think given it will only cover the latter two, it
> should be something like kmalloc_isolation= instead. Let me know what you
> think.
There has been constant, steady efforts to add more and more hardening
features over the last decade. So I'd say slab_hardening= is more
future proof :-)
Kees or Marco might have more insights on this.
--
Cheers,
Harry / Hyeonggon
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-10-28 15:37 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-27 7:35 [PATCH 0/8] mm/slab: Support selecting slab hardening features at boot Chris Down
2026-10-27 7:35 ` [PATCH 1/8] mm/slub: Support disabling freelist hardening " Chris Down
2026-10-27 7:35 ` [PATCH 2/8] slub_kunit: Add a test for slab_freelist_hardened= Chris Down
2026-10-27 7:35 ` [PATCH 3/8] mm/slub: Support disabling freelist randomisation at boot Chris Down
2026-10-27 7:35 ` [PATCH 4/8] slub_kunit: Add a test for slab_freelist_random= Chris Down
2026-10-27 7:35 ` [PATCH 5/8] mm/slab: Support disabling SLAB_BUCKETS at boot Chris Down
2026-10-27 7:36 ` [PATCH 6/8] slub_kunit: Add a test for the slab_buckets boot parameter Chris Down
2026-10-27 7:36 ` [PATCH 7/8] mm/slab: Support disabling KMALLOC_PARTITION_CACHES at boot Chris Down
2026-10-27 7:36 ` [PATCH 8/8] slub_kunit: Add a test for the kmalloc_partition boot parameter Chris Down
2026-10-27 8:10 ` [PATCH 0/8] mm/slab: Support selecting slab hardening features at boot Vlastimil Babka (SUSE)
2026-10-27 18:31 ` Harry Yoo
2026-10-28 15:23 ` Chris Down
2026-10-28 15:06 ` Harry Yoo
2026-10-28 15:27 ` Chris Down
2026-10-28 15:37 ` Harry Yoo
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®