[PATCH RFC v10 01/22] mm: Introduce kpkeys

From: Kevin Brodsky

Date: Mon Nov 09 2026 - 11:12:41 EST


kpkeys is a simple framework to enable the use of protection keys
(pkeys) to harden the kernel itself. This patch introduces the basic
API in <linux/kpkeys.h>: a pair of functions to enter/leave a kpkeys
context, and macros to define guard objects.

Each kpkeys context specifies permissions for the pkeys managed by the
framework. kpkeys_enter_context() applies those permissions and
returns a state for kpkeys_leave_context() to restore.

The saved state is represented by struct kpkeys_state, with one
field for the arch state (typically the saved pkeys register value).
The other field is used to skip the restore step in case we were
already in the context passed to enter_context().

Support for kpkeys must be indicated by selecting ARCH_HAS_KPKEYS.
The architecture must then provide:

* struct arch_kpkeys_state in <asm/kpkeys_types.h>

* arch_kpkeys_enter_context() in <asm/kpkeys.h>:
if needed, sets the new state and saves the original one in
arch_kpkeys_state; reports whether the context was actually
changed

* arch_kpkeys_leave_context() in <asm/kpkeys.h>:
restores the original state

* arch_supports_kpkeys() in <asm/kpkeys.h>:
returns whether the system has an appropriate hardware feature to
support kpkeys

To start with, only KPKEYS_CTX_DEFAULT is available, granting
RW access to the default pkey (0).

Signed-off-by: Kevin Brodsky <kevin.brodsky@xxxxxxx>
---
include/linux/kpkeys.h | 107 +++++++++++++++++++++++++++++++++++++++++++
include/linux/kpkeys_types.h | 25 ++++++++++
mm/Kconfig | 2 +
3 files changed, 134 insertions(+)

diff --git a/include/linux/kpkeys.h b/include/linux/kpkeys.h
new file mode 100644
index 000000000000..69ec4808d850
--- /dev/null
+++ b/include/linux/kpkeys.h
@@ -0,0 +1,107 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _LINUX_KPKEYS_H
+#define _LINUX_KPKEYS_H
+
+#include <linux/bug.h>
+#include <linux/cleanup.h>
+#include <linux/kpkeys_types.h>
+
+/**
+ * KPKEYS_GUARD_NOOP() - helper to define a guard type that does nothing
+ * @name: the name of the guard type
+ *
+ * Define a guard type that does nothing, useful to match a real guard type
+ * that is defined under an #ifdef.
+ */
+#define KPKEYS_GUARD_NOOP(name) \
+ __DEFINE_CLASS_IS_CONDITIONAL(name, false); \
+ DEFINE_CLASS(name, bool, (void)_T, false, void); \
+ static inline void *class_##name##_lock_ptr(bool *_T) \
+ { return _T; }
+
+#ifdef CONFIG_ARCH_HAS_KPKEYS
+
+#include <asm/kpkeys.h>
+
+/**
+ * KPKEYS_GUARD_COND() - helper to define a guard type that conditionally
+ * switches to a given kpkeys context
+ * @name: the name of the guard type
+ * @ctx: the kpkeys context to switch to
+ * @cond: an expression that is evaluated as condition
+ *
+ * Define a guard type that switches to @ctx if @cond evaluates to true,
+ * and does nothing otherwise.
+ */
+#define KPKEYS_GUARD_COND(name, ctx, cond) \
+ __DEFINE_CLASS_IS_CONDITIONAL(name, false); \
+ DEFINE_CLASS(name, struct kpkeys_state, \
+ kpkeys_leave_context(&_T), \
+ (cond) ? kpkeys_enter_context(ctx) : \
+ (struct kpkeys_state) {}, void); \
+ static inline \
+ void *class_##name##_lock_ptr(struct kpkeys_state *_T) \
+ { return _T; }
+
+/**
+ * KPKEYS_GUARD() - helper to define a guard type that switches to a given
+ * kpkeys context if kpkeys are supported
+ * @name: the name of the guard type
+ * @ctx: the kpkeys context to switch to
+ *
+ * Define a guard type that switches to @ctx if the system supports kpkeys.
+ */
+#define KPKEYS_GUARD(name, ctx) \
+ KPKEYS_GUARD_COND(name, ctx, kpkeys_supported())
+
+/**
+ * kpkeys_enter_context() - enter a kpkeys context
+ * @ctx: the context to switch to
+ *
+ * Enters the specified kpkeys context. @ctx must be a compile-time constant.
+ *
+ * Return: state to be passed to kpkeys_leave_context().
+ */
+static __always_inline
+struct kpkeys_state kpkeys_enter_context(enum kpkeys_ctx ctx)
+{
+ struct kpkeys_state state = {};
+
+ BUILD_BUG_ON_MSG(!__builtin_constant_p(ctx),
+ "kpkeys_enter_context() only takes constant values");
+ BUILD_BUG_ON_MSG(ctx < 0 || ctx >= KPKEYS_CTX_COUNT,
+ "Invalid value passed to kpkeys_enter_context()");
+
+ state.entered_context = arch_kpkeys_enter_context(ctx, &state.arch_state);
+ return state;
+}
+
+/**
+ * kpkeys_leave_context() - leave a kpkeys context
+ * @state: state returned by kpkeys_enter_context()
+ *
+ * Restores the state saved when entering a kpkeys context. If no context was
+ * entered, this function does nothing.
+ */
+static __always_inline
+void kpkeys_leave_context(const struct kpkeys_state *state)
+{
+ if (state->entered_context)
+ arch_kpkeys_leave_context(&state->arch_state);
+}
+
+static inline bool kpkeys_supported(void)
+{
+ return arch_supports_kpkeys();
+}
+
+#else /* CONFIG_ARCH_HAS_KPKEYS */
+
+static inline bool kpkeys_supported(void)
+{
+ return false;
+}
+
+#endif /* CONFIG_ARCH_HAS_KPKEYS */
+
+#endif /* _LINUX_KPKEYS_H */
diff --git a/include/linux/kpkeys_types.h b/include/linux/kpkeys_types.h
new file mode 100644
index 000000000000..9de59092e9c3
--- /dev/null
+++ b/include/linux/kpkeys_types.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _LINUX_KPKEYS_TYPES_H
+#define _LINUX_KPKEYS_TYPES_H
+
+#include <linux/types.h>
+
+#ifdef CONFIG_ARCH_HAS_KPKEYS
+#include <asm/kpkeys_types.h>
+#endif
+
+#if defined(CONFIG_ARCH_HAS_KPKEYS) && !defined(__ASSEMBLY__)
+
+enum kpkeys_ctx {
+ KPKEYS_CTX_DEFAULT = 0,
+ KPKEYS_CTX_COUNT,
+};
+
+struct kpkeys_state {
+ bool entered_context;
+ struct arch_kpkeys_state arch_state;
+};
+
+#endif /* CONFIG_ARCH_HAS_KPKEYS && !__ASSEMBLY__ */
+
+#endif /* _LINUX_KPKEYS_TYPES_H */
diff --git a/mm/Kconfig b/mm/Kconfig
index afff1b25b543..4635628de622 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -1265,6 +1265,8 @@ config ARCH_USES_HIGH_VMA_FLAGS
bool
config ARCH_HAS_PKEYS
bool
+config ARCH_HAS_KPKEYS
+ bool

config ARCH_USES_PG_ARCH_2
bool

--
2.56.0