From: Chris Down <chris@chrisdown.name>
To: Vlastimil Babka <vbabka@kernel.org>, Harry Yoo <harry@kernel.org>,
Andrew Morton <akpm@linux-foundation.org>
Cc: Hao Li <hao.li@linux.dev>, Christoph Lameter <cl@gentwo.org>,
David Rientjes <rientjes@google.com>,
Roman Gushchin <roman.gushchin@linux.dev>,
Kees Cook <kees@kernel.org>, Marco Elver <elver@google.com>,
GONG Ruiqi <gongruiqi@huaweicloud.com>,
Jan Kiszka <jan.kiszka@siemens.com>,
Kieran Bingham <kbingham@kernel.org>,
Jonathan Corbet <corbet@lwn.net>,
linux-mm@kvack.org, linux-hardening@vger.kernel.org,
linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org,
kernel-team@meta.com
Subject: [PATCH 1/8] mm/slub: Support disabling freelist hardening at boot
Date: Tue, 27 Oct 2026 15:35:35 +0800 [thread overview]
Message-ID: <d244124ec287b576470a405c828432df69b4e9cb.1793086056.git.chris@chrisdown.name> (raw)
In-Reply-To: <cover.1793086056.git.chris@chrisdown.name>
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
next prev parent reply other threads:[~2026-10-27 7:35 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-27 7:35 [PATCH 0/8] mm/slab: Support selecting slab hardening features " Chris Down
2026-10-27 7:35 ` Chris Down [this message]
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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=d244124ec287b576470a405c828432df69b4e9cb.1793086056.git.chris@chrisdown.name \
--to=chris@chrisdown.name \
--cc=akpm@linux-foundation.org \
--cc=cl@gentwo.org \
--cc=corbet@lwn.net \
--cc=elver@google.com \
--cc=gongruiqi@huaweicloud.com \
--cc=hao.li@linux.dev \
--cc=harry@kernel.org \
--cc=jan.kiszka@siemens.com \
--cc=kbingham@kernel.org \
--cc=kees@kernel.org \
--cc=kernel-team@meta.com \
--cc=linux-doc@vger.kernel.org \
--cc=linux-hardening@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=rientjes@google.com \
--cc=roman.gushchin@linux.dev \
--cc=vbabka@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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®