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


Reply via email to