Key material allocated with kmalloc() shares its slab pages with unrelated allocations. crash_memaction marks memory at page granularity, so smaller objects in shared slab pages can't cleanly be marked or unmarked. This commit adds a secret pool built on kmem_buckets to hold such keys in marked pages.
Allocations from the pool are marked CRASH_MEMACTION_SECRET. The marking compiles to nothing without CONFIG_CRASH_MEMACTION, and stays a no-op at runtime unless "secret" is among the types enabled on the kernel command line. The pool requires SLAB_BUCKETS, which in turn depends on !SLUB_TINY, hence the new dependency for CRASH_MEMACTION. Without SLAB_BUCKETS, allocations fall back to ordinary kmalloc allocations. The pool likewise falls back to the ordinary kmalloc caches when it is used before its initcall runs, or after pool creation failed. Markings are set on object allocation since there's no clean way to hook page allocation in kmem_buckets. This comes at a small per-allocation overhead. Markings are cleared implicitly by the page allocator in post_alloc_hook() when it hands out a reclaimed page to its next owner. Using this pool for secrets has a useful side effect from a defense-in-depth perspective. Where SLAB_BUCKETS is enabled, it keeps secrets away from other kernel data, which makes UAF or out-of-bounds read vulnerabilities less likely to reach secrets. Signed-off-by: Jan Sebastian Götte <[email protected]> Assisted-by: Claude Opus 5 <[email protected]> --- MAINTAINERS | 2 ++ include/linux/secret_pool.h | 47 +++++++++++++++++++++++++++++++++++++++++++++ kernel/Kconfig.kexec | 2 ++ lib/Makefile | 1 + lib/secret_pool.c | 27 ++++++++++++++++++++++++++ 5 files changed, 79 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index c6350fd34f4c..bcb11c2138bc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14260,8 +14260,10 @@ F: fs/proc/vmcore.c F: include/linux/crash_core.h F: include/linux/crash_dump.h F: include/linux/crash_memaction.h +F: include/linux/secret_pool.h F: include/uapi/linux/vmcore.h F: kernel/crash_*.c +F: lib/secret_pool.c KEENE FM RADIO TRANSMITTER DRIVER M: Hans Verkuil <[email protected]> diff --git a/include/linux/secret_pool.h b/include/linux/secret_pool.h new file mode 100644 index 000000000000..aa9296c981c5 --- /dev/null +++ b/include/linux/secret_pool.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_SECRET_POOL_H +#define _LINUX_SECRET_POOL_H + +#include <linux/gfp.h> +#include <linux/numa.h> +#include <linux/slab.h> +#include <linux/types.h> + +void *secret_pool_alloc_node(size_t size, gfp_t flags, int node) + __alloc_size(1); + +static inline __alloc_size(1) void *secret_pool_alloc(size_t size, gfp_t flags) +{ + return secret_pool_alloc_node(size, flags, NUMA_NO_NODE); +} + +static inline __alloc_size(1) void *secret_pool_zalloc_node(size_t size, + gfp_t flags, + int node) +{ + return secret_pool_alloc_node(size, flags | __GFP_ZERO, node); +} + +static inline __alloc_size(1) void *secret_pool_zalloc(size_t size, + gfp_t flags) +{ + return secret_pool_alloc_node(size, flags | __GFP_ZERO, NUMA_NO_NODE); +} + +static inline void secret_pool_free(const void *objp) +{ + kfree_sensitive(objp); +} + +#define secret_pool_alloc_obj(P, ...) \ + __alloc_objs(secret_pool_alloc, default_gfp(__VA_ARGS__), typeof(P), 1) +#define secret_pool_zalloc_obj(P, ...) \ + __alloc_objs(secret_pool_zalloc, default_gfp(__VA_ARGS__), typeof(P), 1) +#define secret_pool_alloc_flex(P, FAM, COUNT, ...) \ + __alloc_flex(secret_pool_alloc, default_gfp(__VA_ARGS__), typeof(P), \ + FAM, COUNT) +#define secret_pool_zalloc_flex(P, FAM, COUNT, ...) \ + __alloc_flex(secret_pool_zalloc, default_gfp(__VA_ARGS__), typeof(P), \ + FAM, COUNT) + +#endif /* _LINUX_SECRET_POOL_H */ diff --git a/kernel/Kconfig.kexec b/kernel/Kconfig.kexec index f7326fa6476b..890c35199bea 100644 --- a/kernel/Kconfig.kexec +++ b/kernel/Kconfig.kexec @@ -186,6 +186,8 @@ config CRASH_MEMACTION bool "Register kdump actions for certain memory ranges" depends on CRASH_DUMP depends on KEXEC_FILE + depends on !SLUB_TINY + select SLAB_BUCKETS depends on ARCH_SUPPORTS_CRASH_MEMACTION help Track pages that may require special handling by the kdump kernel: diff --git a/lib/Makefile b/lib/Makefile index 43421c39d21b..0e69633bd79f 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -60,6 +60,7 @@ obj-y += bcd.o sort.o parser.o debug_locks.o random32.o \ once.o refcount.o rcuref.o usercopy.o errseq.o bucket_locks.o \ generic-radix-tree.o bitmap-str.o obj-y += string_helpers.o +obj-y += secret_pool.o obj-y += hexdump.o obj-$(CONFIG_TEST_HEXDUMP) += test_hexdump.o obj-y += kstrtox.o diff --git a/lib/secret_pool.c b/lib/secret_pool.c new file mode 100644 index 000000000000..62954847091a --- /dev/null +++ b/lib/secret_pool.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/crash_memaction.h> +#include <linux/export.h> +#include <linux/init.h> +#include <linux/secret_pool.h> +#include <linux/slab.h> + +static kmem_buckets * secret_pool __ro_after_init; + +static int __init secret_pool_init(void) +{ + secret_pool = kmem_buckets_create("secret", 0, 0, 0, NULL); + + return 0; +} +core_initcall(secret_pool_init); + +void *secret_pool_alloc_node(size_t size, gfp_t flags, int node) +{ + void *p = kmem_buckets_alloc_node_track_caller(secret_pool, size, + flags, node); + + crash_memaction_mark(p, size, CRASH_MEMACTION_SECRET); + + return p; +} +EXPORT_SYMBOL_GPL(secret_pool_alloc_node); -- 2.55.0

