The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH v4 1/4] pkeys: add API to switch to permissive/zero pkey register
       [not found] <cover.1740403209.git.dvyukov@google.com>
@ 2025-02-24 13:20 ` Dmitry Vyukov
  2025-02-24 19:04   ` Mathieu Desnoyers
  2025-02-24 13:20 ` [PATCH v4 2/4] x86/signal: Use write_permissive_pkey_val() helper Dmitry Vyukov
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 16+ messages in thread
From: Dmitry Vyukov @ 2025-02-24 13:20 UTC (permalink / raw)
  To: mathieu.desnoyers, peterz, boqun.feng, tglx, mingo, bp,
	dave.hansen, hpa, aruna.ramakrishna, elver
  Cc: Dmitry Vyukov, Paul E. McKenney, x86, linux-kernel

The API allows to switch to permissive pkey register that allows accesses
to all PKEYs, and to a value that allows acccess to the 0 (default) PKEY.
This functionality is already used in x86 signal delivery, and will be
needed for rseq.

Signed-off-by: Dmitry Vyukov <dvyukov@google.com>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: "Paul E. McKenney" <paulmck@kernel.org>
Cc: Boqun Feng <boqun.feng@gmail.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Aruna Ramakrishna <aruna.ramakrishna@oracle.com>
Cc: x86@kernel.org
Cc: linux-kernel@vger.kernel.org
Reviewed-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Fixes: d7822b1e24f2 ("rseq: Introduce restartable sequences system call")

---
Changes in v4:
 - Added Fixes tag

Changes in v3:
 - Renamed API functions to write_permissive_pkey_val/write_pkey_val
 - Added enable_zero_pkey_val for rseq
 - Added Reviewed-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>

Changes in v2:
 - Fixed typo in commit description
---
 arch/x86/Kconfig             |  1 +
 arch/x86/include/asm/pkeys.h | 33 +++++++++++++++++++++++++++++++++
 arch/x86/include/asm/pkru.h  | 10 +++++++---
 include/linux/pkeys.h        | 31 +++++++++++++++++++++++++++++++
 mm/Kconfig                   |  2 ++
 5 files changed, 74 insertions(+), 3 deletions(-)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index be2c311f5118d..43af2840d098f 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1881,6 +1881,7 @@ config X86_INTEL_MEMORY_PROTECTION_KEYS
 	depends on X86_64 && (CPU_SUP_INTEL || CPU_SUP_AMD)
 	select ARCH_USES_HIGH_VMA_FLAGS
 	select ARCH_HAS_PKEYS
+	select ARCH_HAS_PERMISSIVE_PKEY
 	help
 	  Memory Protection Keys provides a mechanism for enforcing
 	  page-based protections, but without requiring modification of the
diff --git a/arch/x86/include/asm/pkeys.h b/arch/x86/include/asm/pkeys.h
index 2e6c04d8a45b4..d6e35ab5c3d22 100644
--- a/arch/x86/include/asm/pkeys.h
+++ b/arch/x86/include/asm/pkeys.h
@@ -2,6 +2,8 @@
 #ifndef _ASM_X86_PKEYS_H
 #define _ASM_X86_PKEYS_H
 
+#include "pkru.h"
+
 /*
  * If more than 16 keys are ever supported, a thorough audit
  * will be necessary to ensure that the types that store key
@@ -123,4 +125,35 @@ static inline int vma_pkey(struct vm_area_struct *vma)
 	return (vma->vm_flags & vma_pkey_mask) >> VM_PKEY_SHIFT;
 }
 
+typedef u32 pkey_reg_t;
+
+static inline pkey_reg_t write_permissive_pkey_val(void)
+{
+	return write_pkru(0);
+}
+
+static inline pkey_reg_t enable_zero_pkey_val(void)
+{
+	u32 pkru;
+
+	if (!cpu_feature_enabled(X86_FEATURE_OSPKE))
+		return 0;
+	/*
+	 * WRPKRU is relatively expensive compared to RDPKRU,
+	 * avoid it if possible.
+	 */
+	pkru = rdpkru();
+	if ((pkru & (PKRU_AD_BIT|PKRU_WD_BIT)) != 0)
+		wrpkru(pkru & ~(PKRU_AD_BIT|PKRU_WD_BIT));
+	return pkru;
+
+
+	return write_pkru(0);
+}
+
+static inline void write_pkey_val(pkey_reg_t val)
+{
+	write_pkru(val);
+}
+
 #endif /*_ASM_X86_PKEYS_H */
diff --git a/arch/x86/include/asm/pkru.h b/arch/x86/include/asm/pkru.h
index 74f0a2d34ffdd..b9bf9b7f2753b 100644
--- a/arch/x86/include/asm/pkru.h
+++ b/arch/x86/include/asm/pkru.h
@@ -39,16 +39,20 @@ static inline u32 read_pkru(void)
 	return 0;
 }
 
-static inline void write_pkru(u32 pkru)
+static inline u32 write_pkru(u32 pkru)
 {
+	u32 old_pkru;
+
 	if (!cpu_feature_enabled(X86_FEATURE_OSPKE))
-		return;
+		return 0;
 	/*
 	 * WRPKRU is relatively expensive compared to RDPKRU.
 	 * Avoid WRPKRU when it would not change the value.
 	 */
-	if (pkru != rdpkru())
+	old_pkru = rdpkru();
+	if (pkru != old_pkru)
 		wrpkru(pkru);
+	return old_pkru;
 }
 
 static inline void pkru_write_default(void)
diff --git a/include/linux/pkeys.h b/include/linux/pkeys.h
index 86be8bf27b41b..262d60f6a15f8 100644
--- a/include/linux/pkeys.h
+++ b/include/linux/pkeys.h
@@ -48,4 +48,35 @@ static inline bool arch_pkeys_enabled(void)
 
 #endif /* ! CONFIG_ARCH_HAS_PKEYS */
 
+#ifndef CONFIG_ARCH_HAS_PERMISSIVE_PKEY
+
+/*
+ * Common name for value of the register that controls access to PKEYs
+ * (called differently on different arches: PKRU, POR, AMR).
+ */
+typedef char pkey_reg_t;
+
+/*
+ * Sets PKEY access register to the most permissive value that allows
+ * accesses to all PKEYs. Returns the current value of PKEY register.
+ * Code should generally arrange switching back to the old value
+ * using write_pkey_val(old_value).
+ */
+static inline pkey_reg_t write_permissive_pkey_val(void)
+{
+	return 0;
+}
+
+/*
+ * Sets PKEY access register to a value that allows access to the 0 (default)
+ * PKEY. Returns the current value of PKEY register.
+ */
+static inline pkey_reg_t enable_zero_pkey_val(void)
+{
+	return 0;
+}
+
+static inline void write_pkey_val(pkey_reg_t val) {}
+#endif /* ! CONFIG_ARCH_HAS_PERMISSIVE_PKEY */
+
 #endif /* _LINUX_PKEYS_H */
diff --git a/mm/Kconfig b/mm/Kconfig
index 1b501db064172..9e874f7713a2b 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -1147,6 +1147,8 @@ config ARCH_USES_HIGH_VMA_FLAGS
 	bool
 config ARCH_HAS_PKEYS
 	bool
+config ARCH_HAS_PERMISSIVE_PKEY
+	bool
 
 config ARCH_USES_PG_ARCH_2
 	bool
-- 
2.48.1.601.g30ceb7b040-goog


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v4 2/4] x86/signal: Use write_permissive_pkey_val() helper
       [not found] <cover.1740403209.git.dvyukov@google.com>
  2025-02-24 13:20 ` [PATCH v4 1/4] pkeys: add API to switch to permissive/zero pkey register Dmitry Vyukov
@ 2025-02-24 13:20 ` Dmitry Vyukov
  2025-02-24 19:11   ` Mathieu Desnoyers
  2025-02-24 13:20 ` [PATCH v4 3/4] rseq: Make rseq work with protection keys Dmitry Vyukov
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 16+ messages in thread
From: Dmitry Vyukov @ 2025-02-24 13:20 UTC (permalink / raw)
  To: mathieu.desnoyers, peterz, boqun.feng, tglx, mingo, bp,
	dave.hansen, hpa, aruna.ramakrishna, elver
  Cc: Dmitry Vyukov, Paul E. McKenney, x86, linux-kernel

Use the new switch_to_permissive_pkey_reg() helper instead of the
custom code. No functional changes intended.

Signed-off-by: Dmitry Vyukov <dvyukov@google.com>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: "Paul E. McKenney" <paulmck@kernel.org>
Cc: Boqun Feng <boqun.feng@gmail.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Aruna Ramakrishna <aruna.ramakrishna@oracle.com>
Cc: x86@kernel.org
Cc: linux-kernel@vger.kernel.org

---
Changes in v3:
 - restore sig_prepare_pkru with the large comment and
   make it call the new write_permissive_pkey_val
---
 arch/x86/kernel/signal.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
index 5f441039b5725..27a66a0697dd2 100644
--- a/arch/x86/kernel/signal.c
+++ b/arch/x86/kernel/signal.c
@@ -28,6 +28,7 @@
 #include <linux/entry-common.h>
 #include <linux/syscalls.h>
 #include <linux/rseq.h>
+#include <linux/pkeys.h>
 
 #include <asm/processor.h>
 #include <asm/ucontext.h>
@@ -72,10 +73,7 @@ static inline int is_x32_frame(struct ksignal *ksig)
  */
 static inline u32 sig_prepare_pkru(void)
 {
-	u32 orig_pkru = read_pkru();
-
-	write_pkru(0);
-	return orig_pkru;
+	return write_permissive_pkey_val();
 }
 
 /*
-- 
2.48.1.601.g30ceb7b040-goog


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v4 3/4] rseq: Make rseq work with protection keys
       [not found] <cover.1740403209.git.dvyukov@google.com>
  2025-02-24 13:20 ` [PATCH v4 1/4] pkeys: add API to switch to permissive/zero pkey register Dmitry Vyukov
  2025-02-24 13:20 ` [PATCH v4 2/4] x86/signal: Use write_permissive_pkey_val() helper Dmitry Vyukov
@ 2025-02-24 13:20 ` Dmitry Vyukov
  2025-02-24 19:18   ` Mathieu Desnoyers
  2025-02-24 13:20 ` [PATCH v4 4/4] selftests/rseq: Add test for rseq+pkeys Dmitry Vyukov
  2025-02-24 13:28 ` [PATCH v4 0/4] rseq: Make rseq work with protection keys Dmitry Vyukov
  4 siblings, 1 reply; 16+ messages in thread
From: Dmitry Vyukov @ 2025-02-24 13:20 UTC (permalink / raw)
  To: mathieu.desnoyers, peterz, boqun.feng, tglx, mingo, bp,
	dave.hansen, hpa, aruna.ramakrishna, elver
  Cc: Dmitry Vyukov, Paul E. McKenney, x86, linux-kernel

If an application registers rseq, and ever switches to another pkey
protection (such that the rseq becomes inaccessible), then any
context switch will cause failure in __rseq_handle_notify_resume()
attempting to read/write struct rseq and/or rseq_cs. Since context
switches are asynchronous and are outside of the application control
(not part of the restricted code scope), temporarily switch to
pkey value that allows access to the 0 (default) PKEY.

Signed-off-by: Dmitry Vyukov <dvyukov@google.com>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: "Paul E. McKenney" <paulmck@kernel.org>
Cc: Boqun Feng <boqun.feng@gmail.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Aruna Ramakrishna <aruna.ramakrishna@oracle.com>
Cc: x86@kernel.org
Cc: linux-kernel@vger.kernel.org
Fixes: d7822b1e24f2 ("rseq: Introduce restartable sequences system call")

---
Changes in v4:
 - Added Fixes tag

Changes in v3:
 - simplify control flow to always enable access to 0 pkey

Changes in v2:
 - fixed typos and reworded the comment
---
 kernel/rseq.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/kernel/rseq.c b/kernel/rseq.c
index 2cb16091ec0ae..9d9c976d3b78c 100644
--- a/kernel/rseq.c
+++ b/kernel/rseq.c
@@ -10,6 +10,7 @@
 
 #include <linux/sched.h>
 #include <linux/uaccess.h>
+#include <linux/pkeys.h>
 #include <linux/syscalls.h>
 #include <linux/rseq.h>
 #include <linux/types.h>
@@ -402,11 +403,19 @@ static int rseq_ip_fixup(struct pt_regs *regs)
 void __rseq_handle_notify_resume(struct ksignal *ksig, struct pt_regs *regs)
 {
 	struct task_struct *t = current;
+	pkey_reg_t saved_pkey;
 	int ret, sig;
 
 	if (unlikely(t->flags & PF_EXITING))
 		return;
 
+	/*
+	 * Enable access to the default (0) pkey in case the thread has
+	 * currently disabled access to it and struct rseq/rseq_cs has
+	 * 0 pkey assigned (the only supported value for now).
+	 */
+	saved_pkey = enable_zero_pkey_val();
+
 	/*
 	 * regs is NULL if and only if the caller is in a syscall path.  Skip
 	 * fixup and leave rseq_cs as is so that rseq_sycall() will detect and
@@ -419,9 +428,11 @@ void __rseq_handle_notify_resume(struct ksignal *ksig, struct pt_regs *regs)
 	}
 	if (unlikely(rseq_update_cpu_node_id(t)))
 		goto error;
+	write_pkey_val(saved_pkey);
 	return;
 
 error:
+	write_pkey_val(saved_pkey);
 	sig = ksig ? ksig->sig : 0;
 	force_sigsegv(sig);
 }
-- 
2.48.1.601.g30ceb7b040-goog


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v4 4/4] selftests/rseq: Add test for rseq+pkeys
       [not found] <cover.1740403209.git.dvyukov@google.com>
                   ` (2 preceding siblings ...)
  2025-02-24 13:20 ` [PATCH v4 3/4] rseq: Make rseq work with protection keys Dmitry Vyukov
@ 2025-02-24 13:20 ` Dmitry Vyukov
  2025-02-24 19:48   ` Mathieu Desnoyers
  2025-02-24 13:28 ` [PATCH v4 0/4] rseq: Make rseq work with protection keys Dmitry Vyukov
  4 siblings, 1 reply; 16+ messages in thread
From: Dmitry Vyukov @ 2025-02-24 13:20 UTC (permalink / raw)
  To: mathieu.desnoyers, peterz, boqun.feng, tglx, mingo, bp,
	dave.hansen, hpa, aruna.ramakrishna, elver
  Cc: Dmitry Vyukov, Paul E. McKenney, x86, linux-kernel

Add a test that ensures that PKEY-protected struct rseq_cs
works and does not lead to process kills.

Signed-off-by: Dmitry Vyukov <dvyukov@google.com>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: "Paul E. McKenney" <paulmck@kernel.org>
Cc: Boqun Feng <boqun.feng@gmail.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Aruna Ramakrishna <aruna.ramakrishna@oracle.com>
Cc: x86@kernel.org
Cc: linux-kernel@vger.kernel.org
Acked-by: Dave Hansen <dave.hansen@linux.intel.com>
Fixes: d7822b1e24f2 ("rseq: Introduce restartable sequences system call")

---
Changes in v4:
 - Added Fixes tag

Changes in v3:
 - added Acked-by: Dave Hansen <dave.hansen@linux.intel.com>
 - rework the test to work when only pkey 0 is supported for rseq

Changes in v2:
 - change test to install protected rseq_cs instead of rseq
---
 tools/testing/selftests/rseq/Makefile    |  2 +-
 tools/testing/selftests/rseq/pkey_test.c | 99 ++++++++++++++++++++++++
 tools/testing/selftests/rseq/rseq.h      |  1 +
 3 files changed, 101 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/rseq/Makefile b/tools/testing/selftests/rseq/Makefile
index 5a3432fceb586..9111d25fea3af 100644
--- a/tools/testing/selftests/rseq/Makefile
+++ b/tools/testing/selftests/rseq/Makefile
@@ -16,7 +16,7 @@ OVERRIDE_TARGETS = 1
 
 TEST_GEN_PROGS = basic_test basic_percpu_ops_test basic_percpu_ops_mm_cid_test param_test \
 		param_test_benchmark param_test_compare_twice param_test_mm_cid \
-		param_test_mm_cid_benchmark param_test_mm_cid_compare_twice
+		param_test_mm_cid_benchmark param_test_mm_cid_compare_twice pkey_test
 
 TEST_GEN_PROGS_EXTENDED = librseq.so
 
diff --git a/tools/testing/selftests/rseq/pkey_test.c b/tools/testing/selftests/rseq/pkey_test.c
new file mode 100644
index 0000000000000..8752ecea21ba8
--- /dev/null
+++ b/tools/testing/selftests/rseq/pkey_test.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Ensure that rseq works when rseq data is inaccessible due to PKEYs.
+ */
+
+#define _GNU_SOURCE
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <ucontext.h>
+#include <unistd.h>
+
+#include "rseq.h"
+#include "rseq-abi.h"
+
+int pkey;
+ucontext_t ucp0, ucp1;
+
+void coroutine(void)
+{
+	int i, orig_pk0, old_pk0, old_pk1, pk0, pk1;
+	/*
+	 * When we disable access to pkey 0, globals and TLS become
+	 * inaccessible too, so we need to tread carefully.
+	 * Pkey is global so we need to copy it to onto stack.
+	 * If ts is not volatile, then compiler may try to init it
+	 * by loading a global 16-byte value.
+	 */
+	volatile int pk = pkey;
+	volatile struct timespec ts;
+
+	orig_pk0 = pkey_get(0);
+	if (pkey_set(0, PKEY_DISABLE_ACCESS))
+		err(1, "pkey_set failed");
+	old_pk0 = pkey_get(0);
+	old_pk1 = pkey_get(pk);
+
+	/*
+	 * If the kernel misbehaves, context switches in the following loop
+	 * will terminate the process with SIGSEGV.
+	 */
+	ts.tv_sec = 0;
+	ts.tv_nsec = 10 * 1000;
+	/*
+	 * Trigger preemption w/o accessing TLS.
+	 * Note that glibc's usleep touches errno always.
+	 */
+	for (i = 0; i < 10; i++)
+		syscall(SYS_clock_nanosleep, CLOCK_MONOTONIC, 0, &ts, NULL);
+
+	pk0 = pkey_get(0);
+	pk1 = pkey_get(pk);
+	if (pkey_set(0, orig_pk0))
+		err(1, "pkey_set failed");
+
+	/*
+	 * Ensure that the kernel has restored the previous value of pkeys
+	 * register after changing them.
+	 */
+	if (old_pk0 != pk0)
+		errx(1, "pkey 0 changed %d->%d", old_pk0, pk0);
+	if (old_pk1 != pk1)
+		errx(1, "pkey 1 changed %d->%d", old_pk1, pk1);
+
+	swapcontext(&ucp1, &ucp0);
+	abort();
+}
+
+int main(int argc, char **argv)
+{
+	pkey = pkey_alloc(0, 0);
+	if (pkey == -1) {
+		printf("[SKIP]\tKernel does not support PKEYs: %s\n",
+			strerror(errno));
+		return 0;
+	}
+
+	if (rseq_register_current_thread())
+		err(1, "rseq_register_current_thread failed");
+
+	if (getcontext(&ucp1))
+		err(1, "getcontext failed");
+	ucp1.uc_stack.ss_size = getpagesize() * 4;
+	ucp1.uc_stack.ss_sp = mmap(NULL, ucp1.uc_stack.ss_size,
+		PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
+	if (ucp1.uc_stack.ss_sp == MAP_FAILED)
+		err(1, "mmap failed");
+	if (pkey_mprotect(ucp1.uc_stack.ss_sp, ucp1.uc_stack.ss_size,
+			PROT_READ | PROT_WRITE, pkey))
+		err(1, "pkey_mprotect failed");
+	makecontext(&ucp1, coroutine, 0);
+	if (swapcontext(&ucp0, &ucp1))
+		err(1, "swapcontext failed");
+	return 0;
+}
diff --git a/tools/testing/selftests/rseq/rseq.h b/tools/testing/selftests/rseq/rseq.h
index ba424ce80a719..65da4a727c550 100644
--- a/tools/testing/selftests/rseq/rseq.h
+++ b/tools/testing/selftests/rseq/rseq.h
@@ -8,6 +8,7 @@
 #ifndef RSEQ_H
 #define RSEQ_H
 
+#include <assert.h>
 #include <stdint.h>
 #include <stdbool.h>
 #include <pthread.h>
-- 
2.48.1.601.g30ceb7b040-goog


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 0/4] rseq: Make rseq work with protection keys
       [not found] <cover.1740403209.git.dvyukov@google.com>
                   ` (3 preceding siblings ...)
  2025-02-24 13:20 ` [PATCH v4 4/4] selftests/rseq: Add test for rseq+pkeys Dmitry Vyukov
@ 2025-02-24 13:28 ` Dmitry Vyukov
  4 siblings, 0 replies; 16+ messages in thread
From: Dmitry Vyukov @ 2025-02-24 13:28 UTC (permalink / raw)
  To: mathieu.desnoyers, peterz, boqun.feng, tglx, mingo, bp,
	dave.hansen, hpa, aruna.ramakrishna, elver, LKML,
	the arch/x86 maintainers

On Mon, 24 Feb 2025 at 14:21, Dmitry Vyukov <dvyukov@google.com> wrote:
>
> If an application registers rseq, and ever switches to another pkey
> protection (such that the rseq becomes inaccessible), then any
> context switch will cause failure in __rseq_handle_notify_resume()
> attempting to read/write struct rseq and/or rseq_cs. Since context
> switches are asynchronous and are outside of the application control
> (not part of the restricted code scope), temporarily enable access
> to 0 (default) PKEY to read/write rseq/rseq_cs.
> 0 is the only PKEY supported for rseq for now.
> Theoretically other PKEYs can be supported, but it's unclear
> how/if that can work. So for now we don't support that to simplify
> code.
>
> Dmitry Vyukov (4):
>   pkeys: add API to switch to permissive/zero pkey register
>   x86/signal: Use write_permissive_pkey_val() helper
>   rseq: Make rseq work with protection keys
>   selftests/rseq: Add test for rseq+pkeys
>
>  arch/x86/Kconfig                         |  1 +
>  arch/x86/include/asm/pkeys.h             | 33 ++++++++
>  arch/x86/include/asm/pkru.h              | 10 ++-
>  arch/x86/kernel/signal.c                 |  6 +-
>  include/linux/pkeys.h                    | 31 ++++++++
>  kernel/rseq.c                            | 11 +++
>  mm/Kconfig                               |  2 +
>  tools/testing/selftests/rseq/Makefile    |  2 +-
>  tools/testing/selftests/rseq/pkey_test.c | 99 ++++++++++++++++++++++++
>  tools/testing/selftests/rseq/rseq.h      |  1 +
>  10 files changed, 188 insertions(+), 8 deletions(-)
>  create mode 100644 tools/testing/selftests/rseq/pkey_test.c
>
>
> base-commit: d082ecbc71e9e0bf49883ee4afd435a77a5101b6
> --
> 2.48.1.601.g30ceb7b040-goog

+LKML/x86 lists for archiving purposes

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 1/4] pkeys: add API to switch to permissive/zero pkey register
  2025-02-24 13:20 ` [PATCH v4 1/4] pkeys: add API to switch to permissive/zero pkey register Dmitry Vyukov
@ 2025-02-24 19:04   ` Mathieu Desnoyers
  2025-02-25 13:54     ` Dmitry Vyukov
  0 siblings, 1 reply; 16+ messages in thread
From: Mathieu Desnoyers @ 2025-02-24 19:04 UTC (permalink / raw)
  To: Dmitry Vyukov, peterz, boqun.feng, tglx, mingo, bp, dave.hansen,
	hpa, aruna.ramakrishna, elver
  Cc: Paul E. McKenney, x86, linux-kernel

On 2025-02-24 08:20, Dmitry Vyukov wrote:
> The API allows to switch to permissive pkey register that allows accesses
> to all PKEYs, and to a value that allows acccess to the 0 (default) PKEY.
> This functionality is already used in x86 signal delivery, and will be
> needed for rseq.

AFAIU the signal delivery uses the "allow access to all PKEYs"
approach only, not the "allow access to 0-pkey". It would be
good to clarify this in the commit message.

> 
> Signed-off-by: Dmitry Vyukov <dvyukov@google.com>
> Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
> Cc: Peter Zijlstra <peterz@infradead.org>
> Cc: "Paul E. McKenney" <paulmck@kernel.org>
> Cc: Boqun Feng <boqun.feng@gmail.com>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Borislav Petkov <bp@alien8.de>
> Cc: Dave Hansen <dave.hansen@linux.intel.com>
> Cc: "H. Peter Anvin" <hpa@zytor.com>
> Cc: Aruna Ramakrishna <aruna.ramakrishna@oracle.com>
> Cc: x86@kernel.org
> Cc: linux-kernel@vger.kernel.org
> Reviewed-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
> Fixes: d7822b1e24f2 ("rseq: Introduce restartable sequences system call")
> 
> ---
> Changes in v4:
>   - Added Fixes tag
> 
> Changes in v3:
>   - Renamed API functions to write_permissive_pkey_val/write_pkey_val
>   - Added enable_zero_pkey_val for rseq
>   - Added Reviewed-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
> 
> Changes in v2:
>   - Fixed typo in commit description
> ---
>   arch/x86/Kconfig             |  1 +
>   arch/x86/include/asm/pkeys.h | 33 +++++++++++++++++++++++++++++++++
>   arch/x86/include/asm/pkru.h  | 10 +++++++---
>   include/linux/pkeys.h        | 31 +++++++++++++++++++++++++++++++
>   mm/Kconfig                   |  2 ++
>   5 files changed, 74 insertions(+), 3 deletions(-)
> 
> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
> index be2c311f5118d..43af2840d098f 100644
> --- a/arch/x86/Kconfig
> +++ b/arch/x86/Kconfig
> @@ -1881,6 +1881,7 @@ config X86_INTEL_MEMORY_PROTECTION_KEYS
>   	depends on X86_64 && (CPU_SUP_INTEL || CPU_SUP_AMD)
>   	select ARCH_USES_HIGH_VMA_FLAGS
>   	select ARCH_HAS_PKEYS
> +	select ARCH_HAS_PERMISSIVE_PKEY
>   	help
>   	  Memory Protection Keys provides a mechanism for enforcing
>   	  page-based protections, but without requiring modification of the
> diff --git a/arch/x86/include/asm/pkeys.h b/arch/x86/include/asm/pkeys.h
> index 2e6c04d8a45b4..d6e35ab5c3d22 100644
> --- a/arch/x86/include/asm/pkeys.h
> +++ b/arch/x86/include/asm/pkeys.h
> @@ -2,6 +2,8 @@
>   #ifndef _ASM_X86_PKEYS_H
>   #define _ASM_X86_PKEYS_H
>   
> +#include "pkru.h"
> +
>   /*
>    * If more than 16 keys are ever supported, a thorough audit
>    * will be necessary to ensure that the types that store key
> @@ -123,4 +125,35 @@ static inline int vma_pkey(struct vm_area_struct *vma)
>   	return (vma->vm_flags & vma_pkey_mask) >> VM_PKEY_SHIFT;
>   }
>   
> +typedef u32 pkey_reg_t;
> +
> +static inline pkey_reg_t write_permissive_pkey_val(void)
> +{
> +	return write_pkru(0);
> +}
> +
> +static inline pkey_reg_t enable_zero_pkey_val(void)
> +{
> +	u32 pkru;
> +
> +	if (!cpu_feature_enabled(X86_FEATURE_OSPKE))
> +		return 0;
> +	/*
> +	 * WRPKRU is relatively expensive compared to RDPKRU,
> +	 * avoid it if possible.
> +	 */
> +	pkru = rdpkru();
> +	if ((pkru & (PKRU_AD_BIT|PKRU_WD_BIT)) != 0)
> +		wrpkru(pkru & ~(PKRU_AD_BIT|PKRU_WD_BIT));
> +	return pkru;
> +
> +
> +	return write_pkru(0);

This is dead code. What I am missing ?

With those fixed, please keep my reviewed-by :)

Thanks,

Mathieu

> +}
> +
> +static inline void write_pkey_val(pkey_reg_t val)
> +{
> +	write_pkru(val);
> +}
> +
>   #endif /*_ASM_X86_PKEYS_H */
> diff --git a/arch/x86/include/asm/pkru.h b/arch/x86/include/asm/pkru.h
> index 74f0a2d34ffdd..b9bf9b7f2753b 100644
> --- a/arch/x86/include/asm/pkru.h
> +++ b/arch/x86/include/asm/pkru.h
> @@ -39,16 +39,20 @@ static inline u32 read_pkru(void)
>   	return 0;
>   }
>   
> -static inline void write_pkru(u32 pkru)
> +static inline u32 write_pkru(u32 pkru)
>   {
> +	u32 old_pkru;
> +
>   	if (!cpu_feature_enabled(X86_FEATURE_OSPKE))
> -		return;
> +		return 0;
>   	/*
>   	 * WRPKRU is relatively expensive compared to RDPKRU.
>   	 * Avoid WRPKRU when it would not change the value.
>   	 */
> -	if (pkru != rdpkru())
> +	old_pkru = rdpkru();
> +	if (pkru != old_pkru)
>   		wrpkru(pkru);
> +	return old_pkru;
>   }
>   
>   static inline void pkru_write_default(void)
> diff --git a/include/linux/pkeys.h b/include/linux/pkeys.h
> index 86be8bf27b41b..262d60f6a15f8 100644
> --- a/include/linux/pkeys.h
> +++ b/include/linux/pkeys.h
> @@ -48,4 +48,35 @@ static inline bool arch_pkeys_enabled(void)
>   
>   #endif /* ! CONFIG_ARCH_HAS_PKEYS */
>   
> +#ifndef CONFIG_ARCH_HAS_PERMISSIVE_PKEY
> +
> +/*
> + * Common name for value of the register that controls access to PKEYs
> + * (called differently on different arches: PKRU, POR, AMR).
> + */
> +typedef char pkey_reg_t;
> +
> +/*
> + * Sets PKEY access register to the most permissive value that allows
> + * accesses to all PKEYs. Returns the current value of PKEY register.
> + * Code should generally arrange switching back to the old value
> + * using write_pkey_val(old_value).
> + */
> +static inline pkey_reg_t write_permissive_pkey_val(void)
> +{
> +	return 0;
> +}
> +
> +/*
> + * Sets PKEY access register to a value that allows access to the 0 (default)
> + * PKEY. Returns the current value of PKEY register.
> + */
> +static inline pkey_reg_t enable_zero_pkey_val(void)
> +{
> +	return 0;
> +}
> +
> +static inline void write_pkey_val(pkey_reg_t val) {}
> +#endif /* ! CONFIG_ARCH_HAS_PERMISSIVE_PKEY */
> +
>   #endif /* _LINUX_PKEYS_H */
> diff --git a/mm/Kconfig b/mm/Kconfig
> index 1b501db064172..9e874f7713a2b 100644
> --- a/mm/Kconfig
> +++ b/mm/Kconfig
> @@ -1147,6 +1147,8 @@ config ARCH_USES_HIGH_VMA_FLAGS
>   	bool
>   config ARCH_HAS_PKEYS
>   	bool
> +config ARCH_HAS_PERMISSIVE_PKEY
> +	bool
>   
>   config ARCH_USES_PG_ARCH_2
>   	bool


-- 
Mathieu Desnoyers
EfficiOS Inc.
https://www.efficios.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 2/4] x86/signal: Use write_permissive_pkey_val() helper
  2025-02-24 13:20 ` [PATCH v4 2/4] x86/signal: Use write_permissive_pkey_val() helper Dmitry Vyukov
@ 2025-02-24 19:11   ` Mathieu Desnoyers
  0 siblings, 0 replies; 16+ messages in thread
From: Mathieu Desnoyers @ 2025-02-24 19:11 UTC (permalink / raw)
  To: Dmitry Vyukov, peterz, boqun.feng, tglx, mingo, bp, dave.hansen,
	hpa, aruna.ramakrishna, elver
  Cc: Paul E. McKenney, x86, linux-kernel

On 2025-02-24 08:20, Dmitry Vyukov wrote:
> Use the new switch_to_permissive_pkey_reg() helper instead of the
> custom code. No functional changes intended.
> 
> Signed-off-by: Dmitry Vyukov <dvyukov@google.com>
> Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
> Cc: Peter Zijlstra <peterz@infradead.org>
> Cc: "Paul E. McKenney" <paulmck@kernel.org>
> Cc: Boqun Feng <boqun.feng@gmail.com>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Borislav Petkov <bp@alien8.de>
> Cc: Dave Hansen <dave.hansen@linux.intel.com>
> Cc: "H. Peter Anvin" <hpa@zytor.com>
> Cc: Aruna Ramakrishna <aruna.ramakrishna@oracle.com>
> Cc: x86@kernel.org
> Cc: linux-kernel@vger.kernel.org
> 
> ---
> Changes in v3:
>   - restore sig_prepare_pkru with the large comment and
>     make it call the new write_permissive_pkey_val
> ---
>   arch/x86/kernel/signal.c | 6 ++----
>   1 file changed, 2 insertions(+), 4 deletions(-)
> 
> diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
> index 5f441039b5725..27a66a0697dd2 100644
> --- a/arch/x86/kernel/signal.c
> +++ b/arch/x86/kernel/signal.c
> @@ -28,6 +28,7 @@
>   #include <linux/entry-common.h>
>   #include <linux/syscalls.h>
>   #include <linux/rseq.h>
> +#include <linux/pkeys.h>
>   
>   #include <asm/processor.h>
>   #include <asm/ucontext.h>
> @@ -72,10 +73,7 @@ static inline int is_x32_frame(struct ksignal *ksig)
>    */
>   static inline u32 sig_prepare_pkru(void)
>   {
> -	u32 orig_pkru = read_pkru();
> -
> -	write_pkru(0);
> -	return orig_pkru;
> +	return write_permissive_pkey_val();

Reviewed-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>

Note that I would be curious to see if we could get away with changing
this to enable_zero_pkey_val() without breaking any real-life user.
AFAIU this would remove a WRPKRU from signal delivery, but would require
the signal frames to be pkey-0, which is technically be a new ABI
constraint never expressed before.

Thanks,

Mathieu


>   }
>   
>   /*


-- 
Mathieu Desnoyers
EfficiOS Inc.
https://www.efficios.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 3/4] rseq: Make rseq work with protection keys
  2025-02-24 13:20 ` [PATCH v4 3/4] rseq: Make rseq work with protection keys Dmitry Vyukov
@ 2025-02-24 19:18   ` Mathieu Desnoyers
  2025-02-25 14:07     ` Dmitry Vyukov
  0 siblings, 1 reply; 16+ messages in thread
From: Mathieu Desnoyers @ 2025-02-24 19:18 UTC (permalink / raw)
  To: Dmitry Vyukov, peterz, boqun.feng, tglx, mingo, bp, dave.hansen,
	hpa, aruna.ramakrishna, elver
  Cc: Paul E. McKenney, x86, linux-kernel

On 2025-02-24 08:20, Dmitry Vyukov wrote:
> If an application registers rseq, and ever switches to another pkey
> protection (such that the rseq becomes inaccessible), then any
> context switch will cause failure in __rseq_handle_notify_resume()
> attempting to read/write struct rseq and/or rseq_cs. Since context
> switches are asynchronous and are outside of the application control
> (not part of the restricted code scope), temporarily switch to
> pkey value that allows access to the 0 (default) PKEY.

This is a good start, but the plan Dave and I discussed went further
than this. Those additions are needed:

1) Add validation at rseq registration that the struct rseq is indeed
    pkey-0 memory (return failure if not).

2) The pkey-0 requirement is only for struct rseq, which we can check
    for at rseq registration, and happens to be the fast path. For struct
    rseq_cs, this is not the same tradeoff: we cannot easily check its
    associated pkey because the rseq_cs pointer is updated by userspace
    when entering a critical section. But the good news is that reading
    the content of struct rseq_cs is *not* a fast-path: it's only done
    when preempting/delivering a signal over a thread which has a
    non-NULL rseq_cs pointer.

    Therefore reading the struct rseq_cs content should be done with
    write_permissive_pkey_val(), giving access to all pkeys.

Thanks,

Mathieu

> 
> Signed-off-by: Dmitry Vyukov <dvyukov@google.com>
> Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
> Cc: Peter Zijlstra <peterz@infradead.org>
> Cc: "Paul E. McKenney" <paulmck@kernel.org>
> Cc: Boqun Feng <boqun.feng@gmail.com>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Borislav Petkov <bp@alien8.de>
> Cc: Dave Hansen <dave.hansen@linux.intel.com>
> Cc: "H. Peter Anvin" <hpa@zytor.com>
> Cc: Aruna Ramakrishna <aruna.ramakrishna@oracle.com>
> Cc: x86@kernel.org
> Cc: linux-kernel@vger.kernel.org
> Fixes: d7822b1e24f2 ("rseq: Introduce restartable sequences system call")
> 
> ---
> Changes in v4:
>   - Added Fixes tag
> 
> Changes in v3:
>   - simplify control flow to always enable access to 0 pkey
> 
> Changes in v2:
>   - fixed typos and reworded the comment
> ---
>   kernel/rseq.c | 11 +++++++++++
>   1 file changed, 11 insertions(+)
> 
> diff --git a/kernel/rseq.c b/kernel/rseq.c
> index 2cb16091ec0ae..9d9c976d3b78c 100644
> --- a/kernel/rseq.c
> +++ b/kernel/rseq.c
> @@ -10,6 +10,7 @@
>   
>   #include <linux/sched.h>
>   #include <linux/uaccess.h>
> +#include <linux/pkeys.h>
>   #include <linux/syscalls.h>
>   #include <linux/rseq.h>
>   #include <linux/types.h>
> @@ -402,11 +403,19 @@ static int rseq_ip_fixup(struct pt_regs *regs)
>   void __rseq_handle_notify_resume(struct ksignal *ksig, struct pt_regs *regs)
>   {
>   	struct task_struct *t = current;
> +	pkey_reg_t saved_pkey;
>   	int ret, sig;
>   
>   	if (unlikely(t->flags & PF_EXITING))
>   		return;
>   
> +	/*
> +	 * Enable access to the default (0) pkey in case the thread has
> +	 * currently disabled access to it and struct rseq/rseq_cs has
> +	 * 0 pkey assigned (the only supported value for now).
> +	 */
> +	saved_pkey = enable_zero_pkey_val();
> +
>   	/*
>   	 * regs is NULL if and only if the caller is in a syscall path.  Skip
>   	 * fixup and leave rseq_cs as is so that rseq_sycall() will detect and
> @@ -419,9 +428,11 @@ void __rseq_handle_notify_resume(struct ksignal *ksig, struct pt_regs *regs)
>   	}
>   	if (unlikely(rseq_update_cpu_node_id(t)))
>   		goto error;
> +	write_pkey_val(saved_pkey);
>   	return;
>   
>   error:
> +	write_pkey_val(saved_pkey);
>   	sig = ksig ? ksig->sig : 0;
>   	force_sigsegv(sig);
>   }


-- 
Mathieu Desnoyers
EfficiOS Inc.
https://www.efficios.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 4/4] selftests/rseq: Add test for rseq+pkeys
  2025-02-24 13:20 ` [PATCH v4 4/4] selftests/rseq: Add test for rseq+pkeys Dmitry Vyukov
@ 2025-02-24 19:48   ` Mathieu Desnoyers
  2025-02-25 13:55     ` Dmitry Vyukov
  0 siblings, 1 reply; 16+ messages in thread
From: Mathieu Desnoyers @ 2025-02-24 19:48 UTC (permalink / raw)
  To: Dmitry Vyukov, peterz, boqun.feng, tglx, mingo, bp, dave.hansen,
	hpa, aruna.ramakrishna, elver
  Cc: Paul E. McKenney, x86, linux-kernel

On 2025-02-24 08:20, Dmitry Vyukov wrote:
> Add a test that ensures that PKEY-protected struct rseq_cs
> works and does not lead to process kills.
> 
> Signed-off-by: Dmitry Vyukov <dvyukov@google.com>
> Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
> Cc: Peter Zijlstra <peterz@infradead.org>
> Cc: "Paul E. McKenney" <paulmck@kernel.org>
> Cc: Boqun Feng <boqun.feng@gmail.com>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Borislav Petkov <bp@alien8.de>
> Cc: Dave Hansen <dave.hansen@linux.intel.com>
> Cc: "H. Peter Anvin" <hpa@zytor.com>
> Cc: Aruna Ramakrishna <aruna.ramakrishna@oracle.com>
> Cc: x86@kernel.org
> Cc: linux-kernel@vger.kernel.org
> Acked-by: Dave Hansen <dave.hansen@linux.intel.com>
> Fixes: d7822b1e24f2 ("rseq: Introduce restartable sequences system call")
> 
> ---
> Changes in v4:
>   - Added Fixes tag
> 
> Changes in v3:
>   - added Acked-by: Dave Hansen <dave.hansen@linux.intel.com>
>   - rework the test to work when only pkey 0 is supported for rseq
> 
> Changes in v2:
>   - change test to install protected rseq_cs instead of rseq
> ---
>   tools/testing/selftests/rseq/Makefile    |  2 +-
>   tools/testing/selftests/rseq/pkey_test.c | 99 ++++++++++++++++++++++++
>   tools/testing/selftests/rseq/rseq.h      |  1 +
>   3 files changed, 101 insertions(+), 1 deletion(-)
> 
> diff --git a/tools/testing/selftests/rseq/Makefile b/tools/testing/selftests/rseq/Makefile
> index 5a3432fceb586..9111d25fea3af 100644
> --- a/tools/testing/selftests/rseq/Makefile
> +++ b/tools/testing/selftests/rseq/Makefile
> @@ -16,7 +16,7 @@ OVERRIDE_TARGETS = 1
>   
>   TEST_GEN_PROGS = basic_test basic_percpu_ops_test basic_percpu_ops_mm_cid_test param_test \
>   		param_test_benchmark param_test_compare_twice param_test_mm_cid \
> -		param_test_mm_cid_benchmark param_test_mm_cid_compare_twice
> +		param_test_mm_cid_benchmark param_test_mm_cid_compare_twice pkey_test
>   
>   TEST_GEN_PROGS_EXTENDED = librseq.so
>   
> diff --git a/tools/testing/selftests/rseq/pkey_test.c b/tools/testing/selftests/rseq/pkey_test.c
> new file mode 100644
> index 0000000000000..8752ecea21ba8
> --- /dev/null
> +++ b/tools/testing/selftests/rseq/pkey_test.c
> @@ -0,0 +1,99 @@
> +// SPDX-License-Identifier: LGPL-2.1
> +/*
> + * Ensure that rseq works when rseq data is inaccessible due to PKEYs.
> + */
> +
> +#define _GNU_SOURCE
> +#include <err.h>
> +#include <errno.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/mman.h>
> +#include <sys/syscall.h>
> +#include <ucontext.h>
> +#include <unistd.h>
> +
> +#include "rseq.h"
> +#include "rseq-abi.h"
> +
> +int pkey;
> +ucontext_t ucp0, ucp1;

Why use an external linkage entity rather than static ?

> +
> +void coroutine(void)
> +{
> +	int i, orig_pk0, old_pk0, old_pk1, pk0, pk1;
> +	/*
> +	 * When we disable access to pkey 0, globals and TLS become
> +	 * inaccessible too, so we need to tread carefully.
> +	 * Pkey is global so we need to copy it to onto stack.

to onto -> onto the ?

> +	 * If ts is not volatile, then compiler may try to init it
> +	 * by loading a global 16-byte value.
> +	 */
> +	volatile int pk = pkey;
> +	volatile struct timespec ts;

I think you are looking for RSEQ_READ_ONCE() when loading from the
global variables to prevent re-fetch. AFAIU the volatile on the stack
variables are not what you are looking for.

> +
> +	orig_pk0 = pkey_get(0);
> +	if (pkey_set(0, PKEY_DISABLE_ACCESS))

AFAIU the pkey_set() call needs to act as a memory clobber. Therefore
having RSEQ_READ_ONCE() before the clobber to copy the global variables
onto the stack should be OK.

Thanks,

Mathieu


> +		err(1, "pkey_set failed");
> +	old_pk0 = pkey_get(0);
> +	old_pk1 = pkey_get(pk);
> +
> +	/*
> +	 * If the kernel misbehaves, context switches in the following loop
> +	 * will terminate the process with SIGSEGV.
> +	 */
> +	ts.tv_sec = 0;
> +	ts.tv_nsec = 10 * 1000;
> +	/*
> +	 * Trigger preemption w/o accessing TLS.
> +	 * Note that glibc's usleep touches errno always.
> +	 */
> +	for (i = 0; i < 10; i++)
> +		syscall(SYS_clock_nanosleep, CLOCK_MONOTONIC, 0, &ts, NULL);
> +
> +	pk0 = pkey_get(0);
> +	pk1 = pkey_get(pk);
> +	if (pkey_set(0, orig_pk0))
> +		err(1, "pkey_set failed");
> +
> +	/*
> +	 * Ensure that the kernel has restored the previous value of pkeys
> +	 * register after changing them.
> +	 */
> +	if (old_pk0 != pk0)
> +		errx(1, "pkey 0 changed %d->%d", old_pk0, pk0);
> +	if (old_pk1 != pk1)
> +		errx(1, "pkey 1 changed %d->%d", old_pk1, pk1);
> +
> +	swapcontext(&ucp1, &ucp0);
> +	abort();
> +}
> +
> +int main(int argc, char **argv)
> +{
> +	pkey = pkey_alloc(0, 0);
> +	if (pkey == -1) {
> +		printf("[SKIP]\tKernel does not support PKEYs: %s\n",
> +			strerror(errno));
> +		return 0;
> +	}
> +
> +	if (rseq_register_current_thread())
> +		err(1, "rseq_register_current_thread failed");
> +
> +	if (getcontext(&ucp1))
> +		err(1, "getcontext failed");
> +	ucp1.uc_stack.ss_size = getpagesize() * 4;
> +	ucp1.uc_stack.ss_sp = mmap(NULL, ucp1.uc_stack.ss_size,
> +		PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
> +	if (ucp1.uc_stack.ss_sp == MAP_FAILED)
> +		err(1, "mmap failed");
> +	if (pkey_mprotect(ucp1.uc_stack.ss_sp, ucp1.uc_stack.ss_size,
> +			PROT_READ | PROT_WRITE, pkey))
> +		err(1, "pkey_mprotect failed");
> +	makecontext(&ucp1, coroutine, 0);
> +	if (swapcontext(&ucp0, &ucp1))
> +		err(1, "swapcontext failed");
> +	return 0;
> +}
> diff --git a/tools/testing/selftests/rseq/rseq.h b/tools/testing/selftests/rseq/rseq.h
> index ba424ce80a719..65da4a727c550 100644
> --- a/tools/testing/selftests/rseq/rseq.h
> +++ b/tools/testing/selftests/rseq/rseq.h
> @@ -8,6 +8,7 @@
>   #ifndef RSEQ_H
>   #define RSEQ_H
>   
> +#include <assert.h>
>   #include <stdint.h>
>   #include <stdbool.h>
>   #include <pthread.h>


-- 
Mathieu Desnoyers
EfficiOS Inc.
https://www.efficios.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 1/4] pkeys: add API to switch to permissive/zero pkey register
  2025-02-24 19:04   ` Mathieu Desnoyers
@ 2025-02-25 13:54     ` Dmitry Vyukov
  0 siblings, 0 replies; 16+ messages in thread
From: Dmitry Vyukov @ 2025-02-25 13:54 UTC (permalink / raw)
  To: Mathieu Desnoyers
  Cc: peterz, boqun.feng, tglx, mingo, bp, dave.hansen, hpa,
	aruna.ramakrishna, elver, Paul E. McKenney, x86, linux-kernel

On Mon, 24 Feb 2025 at 20:04, Mathieu Desnoyers
<mathieu.desnoyers@efficios.com> wrote:
>
> On 2025-02-24 08:20, Dmitry Vyukov wrote:
> > The API allows to switch to permissive pkey register that allows accesses
> > to all PKEYs, and to a value that allows acccess to the 0 (default) PKEY.
> > This functionality is already used in x86 signal delivery, and will be
> > needed for rseq.
>
> AFAIU the signal delivery uses the "allow access to all PKEYs"
> approach only, not the "allow access to 0-pkey". It would be
> good to clarify this in the commit message.

Done in v5.

> > Signed-off-by: Dmitry Vyukov <dvyukov@google.com>
> > Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
> > Cc: Peter Zijlstra <peterz@infradead.org>
> > Cc: "Paul E. McKenney" <paulmck@kernel.org>
> > Cc: Boqun Feng <boqun.feng@gmail.com>
> > Cc: Thomas Gleixner <tglx@linutronix.de>
> > Cc: Ingo Molnar <mingo@redhat.com>
> > Cc: Borislav Petkov <bp@alien8.de>
> > Cc: Dave Hansen <dave.hansen@linux.intel.com>
> > Cc: "H. Peter Anvin" <hpa@zytor.com>
> > Cc: Aruna Ramakrishna <aruna.ramakrishna@oracle.com>
> > Cc: x86@kernel.org
> > Cc: linux-kernel@vger.kernel.org
> > Reviewed-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
> > Fixes: d7822b1e24f2 ("rseq: Introduce restartable sequences system call")
> >
> > ---
> > Changes in v4:
> >   - Added Fixes tag
> >
> > Changes in v3:
> >   - Renamed API functions to write_permissive_pkey_val/write_pkey_val
> >   - Added enable_zero_pkey_val for rseq
> >   - Added Reviewed-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
> >
> > Changes in v2:
> >   - Fixed typo in commit description
> > ---
> >   arch/x86/Kconfig             |  1 +
> >   arch/x86/include/asm/pkeys.h | 33 +++++++++++++++++++++++++++++++++
> >   arch/x86/include/asm/pkru.h  | 10 +++++++---
> >   include/linux/pkeys.h        | 31 +++++++++++++++++++++++++++++++
> >   mm/Kconfig                   |  2 ++
> >   5 files changed, 74 insertions(+), 3 deletions(-)
> >
> > diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
> > index be2c311f5118d..43af2840d098f 100644
> > --- a/arch/x86/Kconfig
> > +++ b/arch/x86/Kconfig
> > @@ -1881,6 +1881,7 @@ config X86_INTEL_MEMORY_PROTECTION_KEYS
> >       depends on X86_64 && (CPU_SUP_INTEL || CPU_SUP_AMD)
> >       select ARCH_USES_HIGH_VMA_FLAGS
> >       select ARCH_HAS_PKEYS
> > +     select ARCH_HAS_PERMISSIVE_PKEY
> >       help
> >         Memory Protection Keys provides a mechanism for enforcing
> >         page-based protections, but without requiring modification of the
> > diff --git a/arch/x86/include/asm/pkeys.h b/arch/x86/include/asm/pkeys.h
> > index 2e6c04d8a45b4..d6e35ab5c3d22 100644
> > --- a/arch/x86/include/asm/pkeys.h
> > +++ b/arch/x86/include/asm/pkeys.h
> > @@ -2,6 +2,8 @@
> >   #ifndef _ASM_X86_PKEYS_H
> >   #define _ASM_X86_PKEYS_H
> >
> > +#include "pkru.h"
> > +
> >   /*
> >    * If more than 16 keys are ever supported, a thorough audit
> >    * will be necessary to ensure that the types that store key
> > @@ -123,4 +125,35 @@ static inline int vma_pkey(struct vm_area_struct *vma)
> >       return (vma->vm_flags & vma_pkey_mask) >> VM_PKEY_SHIFT;
> >   }
> >
> > +typedef u32 pkey_reg_t;
> > +
> > +static inline pkey_reg_t write_permissive_pkey_val(void)
> > +{
> > +     return write_pkru(0);
> > +}
> > +
> > +static inline pkey_reg_t enable_zero_pkey_val(void)
> > +{
> > +     u32 pkru;
> > +
> > +     if (!cpu_feature_enabled(X86_FEATURE_OSPKE))
> > +             return 0;
> > +     /*
> > +      * WRPKRU is relatively expensive compared to RDPKRU,
> > +      * avoid it if possible.
> > +      */
> > +     pkru = rdpkru();
> > +     if ((pkru & (PKRU_AD_BIT|PKRU_WD_BIT)) != 0)
> > +             wrpkru(pkru & ~(PKRU_AD_BIT|PKRU_WD_BIT));
> > +     return pkru;
> > +
> > +
> > +     return write_pkru(0);
>
> This is dead code. What I am missing ?

Done in v5.

> With those fixed, please keep my reviewed-by :)
>
> Thanks,
>
> Mathieu
>
> > +}
> > +
> > +static inline void write_pkey_val(pkey_reg_t val)
> > +{
> > +     write_pkru(val);
> > +}
> > +
> >   #endif /*_ASM_X86_PKEYS_H */
> > diff --git a/arch/x86/include/asm/pkru.h b/arch/x86/include/asm/pkru.h
> > index 74f0a2d34ffdd..b9bf9b7f2753b 100644
> > --- a/arch/x86/include/asm/pkru.h
> > +++ b/arch/x86/include/asm/pkru.h
> > @@ -39,16 +39,20 @@ static inline u32 read_pkru(void)
> >       return 0;
> >   }
> >
> > -static inline void write_pkru(u32 pkru)
> > +static inline u32 write_pkru(u32 pkru)
> >   {
> > +     u32 old_pkru;
> > +
> >       if (!cpu_feature_enabled(X86_FEATURE_OSPKE))
> > -             return;
> > +             return 0;
> >       /*
> >        * WRPKRU is relatively expensive compared to RDPKRU.
> >        * Avoid WRPKRU when it would not change the value.
> >        */
> > -     if (pkru != rdpkru())
> > +     old_pkru = rdpkru();
> > +     if (pkru != old_pkru)
> >               wrpkru(pkru);
> > +     return old_pkru;
> >   }
> >
> >   static inline void pkru_write_default(void)
> > diff --git a/include/linux/pkeys.h b/include/linux/pkeys.h
> > index 86be8bf27b41b..262d60f6a15f8 100644
> > --- a/include/linux/pkeys.h
> > +++ b/include/linux/pkeys.h
> > @@ -48,4 +48,35 @@ static inline bool arch_pkeys_enabled(void)
> >
> >   #endif /* ! CONFIG_ARCH_HAS_PKEYS */
> >
> > +#ifndef CONFIG_ARCH_HAS_PERMISSIVE_PKEY
> > +
> > +/*
> > + * Common name for value of the register that controls access to PKEYs
> > + * (called differently on different arches: PKRU, POR, AMR).
> > + */
> > +typedef char pkey_reg_t;
> > +
> > +/*
> > + * Sets PKEY access register to the most permissive value that allows
> > + * accesses to all PKEYs. Returns the current value of PKEY register.
> > + * Code should generally arrange switching back to the old value
> > + * using write_pkey_val(old_value).
> > + */
> > +static inline pkey_reg_t write_permissive_pkey_val(void)
> > +{
> > +     return 0;
> > +}
> > +
> > +/*
> > + * Sets PKEY access register to a value that allows access to the 0 (default)
> > + * PKEY. Returns the current value of PKEY register.
> > + */
> > +static inline pkey_reg_t enable_zero_pkey_val(void)
> > +{
> > +     return 0;
> > +}
> > +
> > +static inline void write_pkey_val(pkey_reg_t val) {}
> > +#endif /* ! CONFIG_ARCH_HAS_PERMISSIVE_PKEY */
> > +
> >   #endif /* _LINUX_PKEYS_H */
> > diff --git a/mm/Kconfig b/mm/Kconfig
> > index 1b501db064172..9e874f7713a2b 100644
> > --- a/mm/Kconfig
> > +++ b/mm/Kconfig
> > @@ -1147,6 +1147,8 @@ config ARCH_USES_HIGH_VMA_FLAGS
> >       bool
> >   config ARCH_HAS_PKEYS
> >       bool
> > +config ARCH_HAS_PERMISSIVE_PKEY
> > +     bool
> >
> >   config ARCH_USES_PG_ARCH_2
> >       bool
>
>
> --
> Mathieu Desnoyers
> EfficiOS Inc.
> https://www.efficios.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 4/4] selftests/rseq: Add test for rseq+pkeys
  2025-02-24 19:48   ` Mathieu Desnoyers
@ 2025-02-25 13:55     ` Dmitry Vyukov
  0 siblings, 0 replies; 16+ messages in thread
From: Dmitry Vyukov @ 2025-02-25 13:55 UTC (permalink / raw)
  To: Mathieu Desnoyers
  Cc: peterz, boqun.feng, tglx, mingo, bp, dave.hansen, hpa,
	aruna.ramakrishna, elver, Paul E. McKenney, x86, linux-kernel

On Mon, 24 Feb 2025 at 20:48, Mathieu Desnoyers
<mathieu.desnoyers@efficios.com> wrote:
>
> On 2025-02-24 08:20, Dmitry Vyukov wrote:
> > Add a test that ensures that PKEY-protected struct rseq_cs
> > works and does not lead to process kills.
> >
> > Signed-off-by: Dmitry Vyukov <dvyukov@google.com>
> > Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
> > Cc: Peter Zijlstra <peterz@infradead.org>
> > Cc: "Paul E. McKenney" <paulmck@kernel.org>
> > Cc: Boqun Feng <boqun.feng@gmail.com>
> > Cc: Thomas Gleixner <tglx@linutronix.de>
> > Cc: Ingo Molnar <mingo@redhat.com>
> > Cc: Borislav Petkov <bp@alien8.de>
> > Cc: Dave Hansen <dave.hansen@linux.intel.com>
> > Cc: "H. Peter Anvin" <hpa@zytor.com>
> > Cc: Aruna Ramakrishna <aruna.ramakrishna@oracle.com>
> > Cc: x86@kernel.org
> > Cc: linux-kernel@vger.kernel.org
> > Acked-by: Dave Hansen <dave.hansen@linux.intel.com>
> > Fixes: d7822b1e24f2 ("rseq: Introduce restartable sequences system call")
> >
> > ---
> > Changes in v4:
> >   - Added Fixes tag
> >
> > Changes in v3:
> >   - added Acked-by: Dave Hansen <dave.hansen@linux.intel.com>
> >   - rework the test to work when only pkey 0 is supported for rseq
> >
> > Changes in v2:
> >   - change test to install protected rseq_cs instead of rseq
> > ---
> >   tools/testing/selftests/rseq/Makefile    |  2 +-
> >   tools/testing/selftests/rseq/pkey_test.c | 99 ++++++++++++++++++++++++
> >   tools/testing/selftests/rseq/rseq.h      |  1 +
> >   3 files changed, 101 insertions(+), 1 deletion(-)
> >
> > diff --git a/tools/testing/selftests/rseq/Makefile b/tools/testing/selftests/rseq/Makefile
> > index 5a3432fceb586..9111d25fea3af 100644
> > --- a/tools/testing/selftests/rseq/Makefile
> > +++ b/tools/testing/selftests/rseq/Makefile
> > @@ -16,7 +16,7 @@ OVERRIDE_TARGETS = 1
> >
> >   TEST_GEN_PROGS = basic_test basic_percpu_ops_test basic_percpu_ops_mm_cid_test param_test \
> >               param_test_benchmark param_test_compare_twice param_test_mm_cid \
> > -             param_test_mm_cid_benchmark param_test_mm_cid_compare_twice
> > +             param_test_mm_cid_benchmark param_test_mm_cid_compare_twice pkey_test
> >
> >   TEST_GEN_PROGS_EXTENDED = librseq.so
> >
> > diff --git a/tools/testing/selftests/rseq/pkey_test.c b/tools/testing/selftests/rseq/pkey_test.c
> > new file mode 100644
> > index 0000000000000..8752ecea21ba8
> > --- /dev/null
> > +++ b/tools/testing/selftests/rseq/pkey_test.c
> > @@ -0,0 +1,99 @@
> > +// SPDX-License-Identifier: LGPL-2.1
> > +/*
> > + * Ensure that rseq works when rseq data is inaccessible due to PKEYs.
> > + */
> > +
> > +#define _GNU_SOURCE
> > +#include <err.h>
> > +#include <errno.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <string.h>
> > +#include <sys/mman.h>
> > +#include <sys/syscall.h>
> > +#include <ucontext.h>
> > +#include <unistd.h>
> > +
> > +#include "rseq.h"
> > +#include "rseq-abi.h"
> > +
> > +int pkey;
> > +ucontext_t ucp0, ucp1;
>
> Why use an external linkage entity rather than static ?

Done in v5.

> > +
> > +void coroutine(void)
> > +{
> > +     int i, orig_pk0, old_pk0, old_pk1, pk0, pk1;
> > +     /*
> > +      * When we disable access to pkey 0, globals and TLS become
> > +      * inaccessible too, so we need to tread carefully.
> > +      * Pkey is global so we need to copy it to onto stack.
>
> to onto -> onto the ?

Done in v5.

> > +      * If ts is not volatile, then compiler may try to init it
> > +      * by loading a global 16-byte value.
> > +      */
> > +     volatile int pk = pkey;
> > +     volatile struct timespec ts;
>
> I think you are looking for RSEQ_READ_ONCE() when loading from the
> global variables to prevent re-fetch. AFAIU the volatile on the stack
> variables are not what you are looking for.

Done in v5.

> > +
> > +     orig_pk0 = pkey_get(0);
> > +     if (pkey_set(0, PKEY_DISABLE_ACCESS))
>
> AFAIU the pkey_set() call needs to act as a memory clobber. Therefore
> having RSEQ_READ_ONCE() before the clobber to copy the global variables
> onto the stack should be OK.

Done in v5.

> Thanks,
>
> Mathieu
>
>
> > +             err(1, "pkey_set failed");
> > +     old_pk0 = pkey_get(0);
> > +     old_pk1 = pkey_get(pk);
> > +
> > +     /*
> > +      * If the kernel misbehaves, context switches in the following loop
> > +      * will terminate the process with SIGSEGV.
> > +      */
> > +     ts.tv_sec = 0;
> > +     ts.tv_nsec = 10 * 1000;
> > +     /*
> > +      * Trigger preemption w/o accessing TLS.
> > +      * Note that glibc's usleep touches errno always.
> > +      */
> > +     for (i = 0; i < 10; i++)
> > +             syscall(SYS_clock_nanosleep, CLOCK_MONOTONIC, 0, &ts, NULL);
> > +
> > +     pk0 = pkey_get(0);
> > +     pk1 = pkey_get(pk);
> > +     if (pkey_set(0, orig_pk0))
> > +             err(1, "pkey_set failed");
> > +
> > +     /*
> > +      * Ensure that the kernel has restored the previous value of pkeys
> > +      * register after changing them.
> > +      */
> > +     if (old_pk0 != pk0)
> > +             errx(1, "pkey 0 changed %d->%d", old_pk0, pk0);
> > +     if (old_pk1 != pk1)
> > +             errx(1, "pkey 1 changed %d->%d", old_pk1, pk1);
> > +
> > +     swapcontext(&ucp1, &ucp0);
> > +     abort();
> > +}
> > +
> > +int main(int argc, char **argv)
> > +{
> > +     pkey = pkey_alloc(0, 0);
> > +     if (pkey == -1) {
> > +             printf("[SKIP]\tKernel does not support PKEYs: %s\n",
> > +                     strerror(errno));
> > +             return 0;
> > +     }
> > +
> > +     if (rseq_register_current_thread())
> > +             err(1, "rseq_register_current_thread failed");
> > +
> > +     if (getcontext(&ucp1))
> > +             err(1, "getcontext failed");
> > +     ucp1.uc_stack.ss_size = getpagesize() * 4;
> > +     ucp1.uc_stack.ss_sp = mmap(NULL, ucp1.uc_stack.ss_size,
> > +             PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
> > +     if (ucp1.uc_stack.ss_sp == MAP_FAILED)
> > +             err(1, "mmap failed");
> > +     if (pkey_mprotect(ucp1.uc_stack.ss_sp, ucp1.uc_stack.ss_size,
> > +                     PROT_READ | PROT_WRITE, pkey))
> > +             err(1, "pkey_mprotect failed");
> > +     makecontext(&ucp1, coroutine, 0);
> > +     if (swapcontext(&ucp0, &ucp1))
> > +             err(1, "swapcontext failed");
> > +     return 0;
> > +}
> > diff --git a/tools/testing/selftests/rseq/rseq.h b/tools/testing/selftests/rseq/rseq.h
> > index ba424ce80a719..65da4a727c550 100644
> > --- a/tools/testing/selftests/rseq/rseq.h
> > +++ b/tools/testing/selftests/rseq/rseq.h
> > @@ -8,6 +8,7 @@
> >   #ifndef RSEQ_H
> >   #define RSEQ_H
> >
> > +#include <assert.h>
> >   #include <stdint.h>
> >   #include <stdbool.h>
> >   #include <pthread.h>
>
>
> --
> Mathieu Desnoyers
> EfficiOS Inc.
> https://www.efficios.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 3/4] rseq: Make rseq work with protection keys
  2025-02-24 19:18   ` Mathieu Desnoyers
@ 2025-02-25 14:07     ` Dmitry Vyukov
  2025-02-25 14:28       ` Mathieu Desnoyers
  0 siblings, 1 reply; 16+ messages in thread
From: Dmitry Vyukov @ 2025-02-25 14:07 UTC (permalink / raw)
  To: Mathieu Desnoyers
  Cc: peterz, boqun.feng, tglx, mingo, bp, dave.hansen, hpa,
	aruna.ramakrishna, elver, Paul E. McKenney, x86, linux-kernel

On Mon, 24 Feb 2025 at 20:18, Mathieu Desnoyers
<mathieu.desnoyers@efficios.com> wrote:
>
> On 2025-02-24 08:20, Dmitry Vyukov wrote:
> > If an application registers rseq, and ever switches to another pkey
> > protection (such that the rseq becomes inaccessible), then any
> > context switch will cause failure in __rseq_handle_notify_resume()
> > attempting to read/write struct rseq and/or rseq_cs. Since context
> > switches are asynchronous and are outside of the application control
> > (not part of the restricted code scope), temporarily switch to
> > pkey value that allows access to the 0 (default) PKEY.
>
> This is a good start, but the plan Dave and I discussed went further
> than this. Those additions are needed:
>
> 1) Add validation at rseq registration that the struct rseq is indeed
>     pkey-0 memory (return failure if not).

I don't think this is worth it for multiple reasons:
 - a program may first register it and then assign a key, which means
we also need to check in pkey_mprotect
 - pkey_mprotect may be applied to rseq of another thread, so ensuring
that will require complex code with non-trivial synchronization and
will add considerable overhead to pkey_mprotect call
 - a program may assign non-0 pkey but have it always accessible, such
programs will break by the new check
 - the misuse is already detected by rseq code, and UNIX errno-based
reporting is not very informative and does not add much value on top
of existing reporting
 - this is not different from registering rseq and then unmap'ing the
memory, checking that does not look like a good idea, and checking
only subset of misuses is inconsistent

Based on my experience with rseq, what would be useful is reporting a
meaningful siginfo for access errors (address/unique code) and fixing
signal delivery. That would solve all of the above problems, and
provide useful info for the user (not just confusing EINVAL from
mprotect/munmap).

But I would prefer to not mix these unrelated usability improvements
and bug fixes with this change. That's not related to this change.


> 2) The pkey-0 requirement is only for struct rseq, which we can check
>     for at rseq registration, and happens to be the fast path. For struct
>     rseq_cs, this is not the same tradeoff: we cannot easily check its
>     associated pkey because the rseq_cs pointer is updated by userspace
>     when entering a critical section. But the good news is that reading
>     the content of struct rseq_cs is *not* a fast-path: it's only done
>     when preempting/delivering a signal over a thread which has a
>     non-NULL rseq_cs pointer.

rseq_cs is usually accessed on a hot path since rseq_cs pointer is not
cleared on critical section exit (at least that's what we do).

>     Therefore reading the struct rseq_cs content should be done with
>     write_permissive_pkey_val(), giving access to all pkeys.

You just asked me to redo the code to simplify it, won't this
complicate it back again? ;)


> Thanks,
>
> Mathieu
>
> >
> > Signed-off-by: Dmitry Vyukov <dvyukov@google.com>
> > Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
> > Cc: Peter Zijlstra <peterz@infradead.org>
> > Cc: "Paul E. McKenney" <paulmck@kernel.org>
> > Cc: Boqun Feng <boqun.feng@gmail.com>
> > Cc: Thomas Gleixner <tglx@linutronix.de>
> > Cc: Ingo Molnar <mingo@redhat.com>
> > Cc: Borislav Petkov <bp@alien8.de>
> > Cc: Dave Hansen <dave.hansen@linux.intel.com>
> > Cc: "H. Peter Anvin" <hpa@zytor.com>
> > Cc: Aruna Ramakrishna <aruna.ramakrishna@oracle.com>
> > Cc: x86@kernel.org
> > Cc: linux-kernel@vger.kernel.org
> > Fixes: d7822b1e24f2 ("rseq: Introduce restartable sequences system call")
> >
> > ---
> > Changes in v4:
> >   - Added Fixes tag
> >
> > Changes in v3:
> >   - simplify control flow to always enable access to 0 pkey
> >
> > Changes in v2:
> >   - fixed typos and reworded the comment
> > ---
> >   kernel/rseq.c | 11 +++++++++++
> >   1 file changed, 11 insertions(+)
> >
> > diff --git a/kernel/rseq.c b/kernel/rseq.c
> > index 2cb16091ec0ae..9d9c976d3b78c 100644
> > --- a/kernel/rseq.c
> > +++ b/kernel/rseq.c
> > @@ -10,6 +10,7 @@
> >
> >   #include <linux/sched.h>
> >   #include <linux/uaccess.h>
> > +#include <linux/pkeys.h>
> >   #include <linux/syscalls.h>
> >   #include <linux/rseq.h>
> >   #include <linux/types.h>
> > @@ -402,11 +403,19 @@ static int rseq_ip_fixup(struct pt_regs *regs)
> >   void __rseq_handle_notify_resume(struct ksignal *ksig, struct pt_regs *regs)
> >   {
> >       struct task_struct *t = current;
> > +     pkey_reg_t saved_pkey;
> >       int ret, sig;
> >
> >       if (unlikely(t->flags & PF_EXITING))
> >               return;
> >
> > +     /*
> > +      * Enable access to the default (0) pkey in case the thread has
> > +      * currently disabled access to it and struct rseq/rseq_cs has
> > +      * 0 pkey assigned (the only supported value for now).
> > +      */
> > +     saved_pkey = enable_zero_pkey_val();
> > +
> >       /*
> >        * regs is NULL if and only if the caller is in a syscall path.  Skip
> >        * fixup and leave rseq_cs as is so that rseq_sycall() will detect and
> > @@ -419,9 +428,11 @@ void __rseq_handle_notify_resume(struct ksignal *ksig, struct pt_regs *regs)
> >       }
> >       if (unlikely(rseq_update_cpu_node_id(t)))
> >               goto error;
> > +     write_pkey_val(saved_pkey);
> >       return;
> >
> >   error:
> > +     write_pkey_val(saved_pkey);
> >       sig = ksig ? ksig->sig : 0;
> >       force_sigsegv(sig);
> >   }
>
>
> --
> Mathieu Desnoyers
> EfficiOS Inc.
> https://www.efficios.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 3/4] rseq: Make rseq work with protection keys
  2025-02-25 14:07     ` Dmitry Vyukov
@ 2025-02-25 14:28       ` Mathieu Desnoyers
  2025-02-25 14:51         ` Dmitry Vyukov
  0 siblings, 1 reply; 16+ messages in thread
From: Mathieu Desnoyers @ 2025-02-25 14:28 UTC (permalink / raw)
  To: Dmitry Vyukov
  Cc: peterz, boqun.feng, tglx, mingo, bp, dave.hansen, hpa,
	aruna.ramakrishna, elver, Paul E. McKenney, x86, linux-kernel

On 2025-02-25 09:07, Dmitry Vyukov wrote:
> On Mon, 24 Feb 2025 at 20:18, Mathieu Desnoyers
> <mathieu.desnoyers@efficios.com> wrote:
>>
>> On 2025-02-24 08:20, Dmitry Vyukov wrote:
>>> If an application registers rseq, and ever switches to another pkey
>>> protection (such that the rseq becomes inaccessible), then any
>>> context switch will cause failure in __rseq_handle_notify_resume()
>>> attempting to read/write struct rseq and/or rseq_cs. Since context
>>> switches are asynchronous and are outside of the application control
>>> (not part of the restricted code scope), temporarily switch to
>>> pkey value that allows access to the 0 (default) PKEY.
>>
>> This is a good start, but the plan Dave and I discussed went further
>> than this. Those additions are needed:
>>
>> 1) Add validation at rseq registration that the struct rseq is indeed
>>      pkey-0 memory (return failure if not).
> 
> I don't think this is worth it for multiple reasons:
>   - a program may first register it and then assign a key, which means
> we also need to check in pkey_mprotect
>   - pkey_mprotect may be applied to rseq of another thread, so ensuring
> that will require complex code with non-trivial synchronization and
> will add considerable overhead to pkey_mprotect call
>   - a program may assign non-0 pkey but have it always accessible, such
> programs will break by the new check
>   - the misuse is already detected by rseq code, and UNIX errno-based
> reporting is not very informative and does not add much value on top
> of existing reporting
>   - this is not different from registering rseq and then unmap'ing the
> memory, checking that does not look like a good idea, and checking
> only subset of misuses is inconsistent
> 
> Based on my experience with rseq, what would be useful is reporting a
> meaningful siginfo for access errors (address/unique code) and fixing
> signal delivery. That would solve all of the above problems, and
> provide useful info for the user (not just confusing EINVAL from
> mprotect/munmap).
> 
> But I would prefer to not mix these unrelated usability improvements
> and bug fixes with this change. That's not related to this change.

I agree with your arguments. If Dave is OK with it, I'd be fine with
leaving out the pkey-0 validation on rseq registration, and eventually
bring meaningful siginfo access errors as future improvements.

So the new behavior would be that both rseq and rseq_cs are required
to be pkey-0. If they are not and their pkey is not accessible in the
current context, it would trigger a segmentation fault. Ideally we'd
want to document this somewhere in the UAPI header.

> 
> 
>> 2) The pkey-0 requirement is only for struct rseq, which we can check
>>      for at rseq registration, and happens to be the fast path. For struct
>>      rseq_cs, this is not the same tradeoff: we cannot easily check its
>>      associated pkey because the rseq_cs pointer is updated by userspace
>>      when entering a critical section. But the good news is that reading
>>      the content of struct rseq_cs is *not* a fast-path: it's only done
>>      when preempting/delivering a signal over a thread which has a
>>      non-NULL rseq_cs pointer.
> 
> rseq_cs is usually accessed on a hot path since rseq_cs pointer is not
> cleared on critical section exit (at least that's what we do).

Fair point.

> 
>>      Therefore reading the struct rseq_cs content should be done with
>>      write_permissive_pkey_val(), giving access to all pkeys.
> 
> You just asked me to redo the code to simplify it, won't this
> complicate it back again? ;)

I'm fine with the pkey-0 approach for both rseq and rseq_cs if Dave is
also OK with it.

Thanks,

Mathieu

> 
> 
>> Thanks,
>>
>> Mathieu
>>
>>>
>>> Signed-off-by: Dmitry Vyukov <dvyukov@google.com>
>>> Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
>>> Cc: Peter Zijlstra <peterz@infradead.org>
>>> Cc: "Paul E. McKenney" <paulmck@kernel.org>
>>> Cc: Boqun Feng <boqun.feng@gmail.com>
>>> Cc: Thomas Gleixner <tglx@linutronix.de>
>>> Cc: Ingo Molnar <mingo@redhat.com>
>>> Cc: Borislav Petkov <bp@alien8.de>
>>> Cc: Dave Hansen <dave.hansen@linux.intel.com>
>>> Cc: "H. Peter Anvin" <hpa@zytor.com>
>>> Cc: Aruna Ramakrishna <aruna.ramakrishna@oracle.com>
>>> Cc: x86@kernel.org
>>> Cc: linux-kernel@vger.kernel.org
>>> Fixes: d7822b1e24f2 ("rseq: Introduce restartable sequences system call")
>>>
>>> ---
>>> Changes in v4:
>>>    - Added Fixes tag
>>>
>>> Changes in v3:
>>>    - simplify control flow to always enable access to 0 pkey
>>>
>>> Changes in v2:
>>>    - fixed typos and reworded the comment
>>> ---
>>>    kernel/rseq.c | 11 +++++++++++
>>>    1 file changed, 11 insertions(+)
>>>
>>> diff --git a/kernel/rseq.c b/kernel/rseq.c
>>> index 2cb16091ec0ae..9d9c976d3b78c 100644
>>> --- a/kernel/rseq.c
>>> +++ b/kernel/rseq.c
>>> @@ -10,6 +10,7 @@
>>>
>>>    #include <linux/sched.h>
>>>    #include <linux/uaccess.h>
>>> +#include <linux/pkeys.h>
>>>    #include <linux/syscalls.h>
>>>    #include <linux/rseq.h>
>>>    #include <linux/types.h>
>>> @@ -402,11 +403,19 @@ static int rseq_ip_fixup(struct pt_regs *regs)
>>>    void __rseq_handle_notify_resume(struct ksignal *ksig, struct pt_regs *regs)
>>>    {
>>>        struct task_struct *t = current;
>>> +     pkey_reg_t saved_pkey;
>>>        int ret, sig;
>>>
>>>        if (unlikely(t->flags & PF_EXITING))
>>>                return;
>>>
>>> +     /*
>>> +      * Enable access to the default (0) pkey in case the thread has
>>> +      * currently disabled access to it and struct rseq/rseq_cs has
>>> +      * 0 pkey assigned (the only supported value for now).
>>> +      */
>>> +     saved_pkey = enable_zero_pkey_val();
>>> +
>>>        /*
>>>         * regs is NULL if and only if the caller is in a syscall path.  Skip
>>>         * fixup and leave rseq_cs as is so that rseq_sycall() will detect and
>>> @@ -419,9 +428,11 @@ void __rseq_handle_notify_resume(struct ksignal *ksig, struct pt_regs *regs)
>>>        }
>>>        if (unlikely(rseq_update_cpu_node_id(t)))
>>>                goto error;
>>> +     write_pkey_val(saved_pkey);
>>>        return;
>>>
>>>    error:
>>> +     write_pkey_val(saved_pkey);
>>>        sig = ksig ? ksig->sig : 0;
>>>        force_sigsegv(sig);
>>>    }
>>
>>
>> --
>> Mathieu Desnoyers
>> EfficiOS Inc.
>> https://www.efficios.com


-- 
Mathieu Desnoyers
EfficiOS Inc.
https://www.efficios.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 3/4] rseq: Make rseq work with protection keys
  2025-02-25 14:28       ` Mathieu Desnoyers
@ 2025-02-25 14:51         ` Dmitry Vyukov
  2025-02-25 14:53           ` Mathieu Desnoyers
  0 siblings, 1 reply; 16+ messages in thread
From: Dmitry Vyukov @ 2025-02-25 14:51 UTC (permalink / raw)
  To: Mathieu Desnoyers
  Cc: peterz, boqun.feng, tglx, mingo, bp, dave.hansen, hpa,
	aruna.ramakrishna, elver, Paul E. McKenney, x86, linux-kernel

On Tue, 25 Feb 2025 at 15:28, Mathieu Desnoyers
<mathieu.desnoyers@efficios.com> wrote:
>
> On 2025-02-25 09:07, Dmitry Vyukov wrote:
> > On Mon, 24 Feb 2025 at 20:18, Mathieu Desnoyers
> > <mathieu.desnoyers@efficios.com> wrote:
> >>
> >> On 2025-02-24 08:20, Dmitry Vyukov wrote:
> >>> If an application registers rseq, and ever switches to another pkey
> >>> protection (such that the rseq becomes inaccessible), then any
> >>> context switch will cause failure in __rseq_handle_notify_resume()
> >>> attempting to read/write struct rseq and/or rseq_cs. Since context
> >>> switches are asynchronous and are outside of the application control
> >>> (not part of the restricted code scope), temporarily switch to
> >>> pkey value that allows access to the 0 (default) PKEY.
> >>
> >> This is a good start, but the plan Dave and I discussed went further
> >> than this. Those additions are needed:
> >>
> >> 1) Add validation at rseq registration that the struct rseq is indeed
> >>      pkey-0 memory (return failure if not).
> >
> > I don't think this is worth it for multiple reasons:
> >   - a program may first register it and then assign a key, which means
> > we also need to check in pkey_mprotect
> >   - pkey_mprotect may be applied to rseq of another thread, so ensuring
> > that will require complex code with non-trivial synchronization and
> > will add considerable overhead to pkey_mprotect call
> >   - a program may assign non-0 pkey but have it always accessible, such
> > programs will break by the new check
> >   - the misuse is already detected by rseq code, and UNIX errno-based
> > reporting is not very informative and does not add much value on top
> > of existing reporting
> >   - this is not different from registering rseq and then unmap'ing the
> > memory, checking that does not look like a good idea, and checking
> > only subset of misuses is inconsistent
> >
> > Based on my experience with rseq, what would be useful is reporting a
> > meaningful siginfo for access errors (address/unique code) and fixing
> > signal delivery. That would solve all of the above problems, and
> > provide useful info for the user (not just confusing EINVAL from
> > mprotect/munmap).
> >
> > But I would prefer to not mix these unrelated usability improvements
> > and bug fixes with this change. That's not related to this change.
>
> I agree with your arguments. If Dave is OK with it, I'd be fine with
> leaving out the pkey-0 validation on rseq registration, and eventually
> bring meaningful siginfo access errors as future improvements.
>
> So the new behavior would be that both rseq and rseq_cs are required
> to be pkey-0. If they are not and their pkey is not accessible in the
> current context, it would trigger a segmentation fault. Ideally we'd
> want to document this somewhere in the UAPI header.

Makes sense. I will wait for Dave comments/ack before sending v6. But
to save a round-trip, does this look reasonable?

--- a/include/uapi/linux/rseq.h
+++ b/include/uapi/linux/rseq.h
@@ -58,6 +58,10 @@ struct rseq_cs {
  * contained within a single cache-line.
  *
  * A single struct rseq per thread is allowed.
+ *
+ * If struct rseq or struct rseq_cs is used with Memory Protection Keys,
+ * then the assigned pkey should either be accessible whenever these structs
+ * are registered/installed, or they should be protected with pkey 0.
  */
 struct rseq {



> >> 2) The pkey-0 requirement is only for struct rseq, which we can check
> >>      for at rseq registration, and happens to be the fast path. For struct
> >>      rseq_cs, this is not the same tradeoff: we cannot easily check its
> >>      associated pkey because the rseq_cs pointer is updated by userspace
> >>      when entering a critical section. But the good news is that reading
> >>      the content of struct rseq_cs is *not* a fast-path: it's only done
> >>      when preempting/delivering a signal over a thread which has a
> >>      non-NULL rseq_cs pointer.
> >
> > rseq_cs is usually accessed on a hot path since rseq_cs pointer is not
> > cleared on critical section exit (at least that's what we do).
>
> Fair point.
>
> >
> >>      Therefore reading the struct rseq_cs content should be done with
> >>      write_permissive_pkey_val(), giving access to all pkeys.
> >
> > You just asked me to redo the code to simplify it, won't this
> > complicate it back again? ;)
>
> I'm fine with the pkey-0 approach for both rseq and rseq_cs if Dave is
> also OK with it.

It should work for my current use case, at least how I currently see
it. Ways people use pkeys are pretty unique, so it's hard to
extrapolate. But there is one more possibility: when a program
switches PKEYs, it may also clear stale rseq_cs pointer from rseq.
This way rseq_cs may have non-0 keys assigned, but they are always
accessible while installed.



> Thanks,
>
> Mathieu
>
> >
> >
> >> Thanks,
> >>
> >> Mathieu
> >>
> >>>
> >>> Signed-off-by: Dmitry Vyukov <dvyukov@google.com>
> >>> Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
> >>> Cc: Peter Zijlstra <peterz@infradead.org>
> >>> Cc: "Paul E. McKenney" <paulmck@kernel.org>
> >>> Cc: Boqun Feng <boqun.feng@gmail.com>
> >>> Cc: Thomas Gleixner <tglx@linutronix.de>
> >>> Cc: Ingo Molnar <mingo@redhat.com>
> >>> Cc: Borislav Petkov <bp@alien8.de>
> >>> Cc: Dave Hansen <dave.hansen@linux.intel.com>
> >>> Cc: "H. Peter Anvin" <hpa@zytor.com>
> >>> Cc: Aruna Ramakrishna <aruna.ramakrishna@oracle.com>
> >>> Cc: x86@kernel.org
> >>> Cc: linux-kernel@vger.kernel.org
> >>> Fixes: d7822b1e24f2 ("rseq: Introduce restartable sequences system call")
> >>>
> >>> ---
> >>> Changes in v4:
> >>>    - Added Fixes tag
> >>>
> >>> Changes in v3:
> >>>    - simplify control flow to always enable access to 0 pkey
> >>>
> >>> Changes in v2:
> >>>    - fixed typos and reworded the comment
> >>> ---
> >>>    kernel/rseq.c | 11 +++++++++++
> >>>    1 file changed, 11 insertions(+)
> >>>
> >>> diff --git a/kernel/rseq.c b/kernel/rseq.c
> >>> index 2cb16091ec0ae..9d9c976d3b78c 100644
> >>> --- a/kernel/rseq.c
> >>> +++ b/kernel/rseq.c
> >>> @@ -10,6 +10,7 @@
> >>>
> >>>    #include <linux/sched.h>
> >>>    #include <linux/uaccess.h>
> >>> +#include <linux/pkeys.h>
> >>>    #include <linux/syscalls.h>
> >>>    #include <linux/rseq.h>
> >>>    #include <linux/types.h>
> >>> @@ -402,11 +403,19 @@ static int rseq_ip_fixup(struct pt_regs *regs)
> >>>    void __rseq_handle_notify_resume(struct ksignal *ksig, struct pt_regs *regs)
> >>>    {
> >>>        struct task_struct *t = current;
> >>> +     pkey_reg_t saved_pkey;
> >>>        int ret, sig;
> >>>
> >>>        if (unlikely(t->flags & PF_EXITING))
> >>>                return;
> >>>
> >>> +     /*
> >>> +      * Enable access to the default (0) pkey in case the thread has
> >>> +      * currently disabled access to it and struct rseq/rseq_cs has
> >>> +      * 0 pkey assigned (the only supported value for now).
> >>> +      */
> >>> +     saved_pkey = enable_zero_pkey_val();
> >>> +
> >>>        /*
> >>>         * regs is NULL if and only if the caller is in a syscall path.  Skip
> >>>         * fixup and leave rseq_cs as is so that rseq_sycall() will detect and
> >>> @@ -419,9 +428,11 @@ void __rseq_handle_notify_resume(struct ksignal *ksig, struct pt_regs *regs)
> >>>        }
> >>>        if (unlikely(rseq_update_cpu_node_id(t)))
> >>>                goto error;
> >>> +     write_pkey_val(saved_pkey);
> >>>        return;
> >>>
> >>>    error:
> >>> +     write_pkey_val(saved_pkey);
> >>>        sig = ksig ? ksig->sig : 0;
> >>>        force_sigsegv(sig);
> >>>    }

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 3/4] rseq: Make rseq work with protection keys
  2025-02-25 14:51         ` Dmitry Vyukov
@ 2025-02-25 14:53           ` Mathieu Desnoyers
  2025-02-27 14:03             ` Dmitry Vyukov
  0 siblings, 1 reply; 16+ messages in thread
From: Mathieu Desnoyers @ 2025-02-25 14:53 UTC (permalink / raw)
  To: Dmitry Vyukov
  Cc: peterz, boqun.feng, tglx, mingo, bp, dave.hansen, hpa,
	aruna.ramakrishna, elver, Paul E. McKenney, x86, linux-kernel

On 2025-02-25 09:51, Dmitry Vyukov wrote:
> On Tue, 25 Feb 2025 at 15:28, Mathieu Desnoyers
> <mathieu.desnoyers@efficios.com> wrote:
>>
>> On 2025-02-25 09:07, Dmitry Vyukov wrote:
>>> On Mon, 24 Feb 2025 at 20:18, Mathieu Desnoyers
>>> <mathieu.desnoyers@efficios.com> wrote:
>>>>
>>>> On 2025-02-24 08:20, Dmitry Vyukov wrote:
>>>>> If an application registers rseq, and ever switches to another pkey
>>>>> protection (such that the rseq becomes inaccessible), then any
>>>>> context switch will cause failure in __rseq_handle_notify_resume()
>>>>> attempting to read/write struct rseq and/or rseq_cs. Since context
>>>>> switches are asynchronous and are outside of the application control
>>>>> (not part of the restricted code scope), temporarily switch to
>>>>> pkey value that allows access to the 0 (default) PKEY.
>>>>
>>>> This is a good start, but the plan Dave and I discussed went further
>>>> than this. Those additions are needed:
>>>>
>>>> 1) Add validation at rseq registration that the struct rseq is indeed
>>>>       pkey-0 memory (return failure if not).
>>>
>>> I don't think this is worth it for multiple reasons:
>>>    - a program may first register it and then assign a key, which means
>>> we also need to check in pkey_mprotect
>>>    - pkey_mprotect may be applied to rseq of another thread, so ensuring
>>> that will require complex code with non-trivial synchronization and
>>> will add considerable overhead to pkey_mprotect call
>>>    - a program may assign non-0 pkey but have it always accessible, such
>>> programs will break by the new check
>>>    - the misuse is already detected by rseq code, and UNIX errno-based
>>> reporting is not very informative and does not add much value on top
>>> of existing reporting
>>>    - this is not different from registering rseq and then unmap'ing the
>>> memory, checking that does not look like a good idea, and checking
>>> only subset of misuses is inconsistent
>>>
>>> Based on my experience with rseq, what would be useful is reporting a
>>> meaningful siginfo for access errors (address/unique code) and fixing
>>> signal delivery. That would solve all of the above problems, and
>>> provide useful info for the user (not just confusing EINVAL from
>>> mprotect/munmap).
>>>
>>> But I would prefer to not mix these unrelated usability improvements
>>> and bug fixes with this change. That's not related to this change.
>>
>> I agree with your arguments. If Dave is OK with it, I'd be fine with
>> leaving out the pkey-0 validation on rseq registration, and eventually
>> bring meaningful siginfo access errors as future improvements.
>>
>> So the new behavior would be that both rseq and rseq_cs are required
>> to be pkey-0. If they are not and their pkey is not accessible in the
>> current context, it would trigger a segmentation fault. Ideally we'd
>> want to document this somewhere in the UAPI header.
> 
> Makes sense. I will wait for Dave comments/ack before sending v6. But
> to save a round-trip, does this look reasonable?
> 
> --- a/include/uapi/linux/rseq.h
> +++ b/include/uapi/linux/rseq.h
> @@ -58,6 +58,10 @@ struct rseq_cs {
>    * contained within a single cache-line.
>    *
>    * A single struct rseq per thread is allowed.
> + *
> + * If struct rseq or struct rseq_cs is used with Memory Protection Keys,
> + * then the assigned pkey should either be accessible whenever these structs
> + * are registered/installed, or they should be protected with pkey 0.

The wording is OK with me.

Thanks,

Mathieu

>    */
>   struct rseq {
> 
> 
> 
>>>> 2) The pkey-0 requirement is only for struct rseq, which we can check
>>>>       for at rseq registration, and happens to be the fast path. For struct
>>>>       rseq_cs, this is not the same tradeoff: we cannot easily check its
>>>>       associated pkey because the rseq_cs pointer is updated by userspace
>>>>       when entering a critical section. But the good news is that reading
>>>>       the content of struct rseq_cs is *not* a fast-path: it's only done
>>>>       when preempting/delivering a signal over a thread which has a
>>>>       non-NULL rseq_cs pointer.
>>>
>>> rseq_cs is usually accessed on a hot path since rseq_cs pointer is not
>>> cleared on critical section exit (at least that's what we do).
>>
>> Fair point.
>>
>>>
>>>>       Therefore reading the struct rseq_cs content should be done with
>>>>       write_permissive_pkey_val(), giving access to all pkeys.
>>>
>>> You just asked me to redo the code to simplify it, won't this
>>> complicate it back again? ;)
>>
>> I'm fine with the pkey-0 approach for both rseq and rseq_cs if Dave is
>> also OK with it.
> 
> It should work for my current use case, at least how I currently see
> it. Ways people use pkeys are pretty unique, so it's hard to
> extrapolate. But there is one more possibility: when a program
> switches PKEYs, it may also clear stale rseq_cs pointer from rseq.
> This way rseq_cs may have non-0 keys assigned, but they are always
> accessible while installed.
> 
> 
> 
>> Thanks,
>>
>> Mathieu
>>
>>>
>>>
>>>> Thanks,
>>>>
>>>> Mathieu
>>>>
>>>>>
>>>>> Signed-off-by: Dmitry Vyukov <dvyukov@google.com>
>>>>> Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
>>>>> Cc: Peter Zijlstra <peterz@infradead.org>
>>>>> Cc: "Paul E. McKenney" <paulmck@kernel.org>
>>>>> Cc: Boqun Feng <boqun.feng@gmail.com>
>>>>> Cc: Thomas Gleixner <tglx@linutronix.de>
>>>>> Cc: Ingo Molnar <mingo@redhat.com>
>>>>> Cc: Borislav Petkov <bp@alien8.de>
>>>>> Cc: Dave Hansen <dave.hansen@linux.intel.com>
>>>>> Cc: "H. Peter Anvin" <hpa@zytor.com>
>>>>> Cc: Aruna Ramakrishna <aruna.ramakrishna@oracle.com>
>>>>> Cc: x86@kernel.org
>>>>> Cc: linux-kernel@vger.kernel.org
>>>>> Fixes: d7822b1e24f2 ("rseq: Introduce restartable sequences system call")
>>>>>
>>>>> ---
>>>>> Changes in v4:
>>>>>     - Added Fixes tag
>>>>>
>>>>> Changes in v3:
>>>>>     - simplify control flow to always enable access to 0 pkey
>>>>>
>>>>> Changes in v2:
>>>>>     - fixed typos and reworded the comment
>>>>> ---
>>>>>     kernel/rseq.c | 11 +++++++++++
>>>>>     1 file changed, 11 insertions(+)
>>>>>
>>>>> diff --git a/kernel/rseq.c b/kernel/rseq.c
>>>>> index 2cb16091ec0ae..9d9c976d3b78c 100644
>>>>> --- a/kernel/rseq.c
>>>>> +++ b/kernel/rseq.c
>>>>> @@ -10,6 +10,7 @@
>>>>>
>>>>>     #include <linux/sched.h>
>>>>>     #include <linux/uaccess.h>
>>>>> +#include <linux/pkeys.h>
>>>>>     #include <linux/syscalls.h>
>>>>>     #include <linux/rseq.h>
>>>>>     #include <linux/types.h>
>>>>> @@ -402,11 +403,19 @@ static int rseq_ip_fixup(struct pt_regs *regs)
>>>>>     void __rseq_handle_notify_resume(struct ksignal *ksig, struct pt_regs *regs)
>>>>>     {
>>>>>         struct task_struct *t = current;
>>>>> +     pkey_reg_t saved_pkey;
>>>>>         int ret, sig;
>>>>>
>>>>>         if (unlikely(t->flags & PF_EXITING))
>>>>>                 return;
>>>>>
>>>>> +     /*
>>>>> +      * Enable access to the default (0) pkey in case the thread has
>>>>> +      * currently disabled access to it and struct rseq/rseq_cs has
>>>>> +      * 0 pkey assigned (the only supported value for now).
>>>>> +      */
>>>>> +     saved_pkey = enable_zero_pkey_val();
>>>>> +
>>>>>         /*
>>>>>          * regs is NULL if and only if the caller is in a syscall path.  Skip
>>>>>          * fixup and leave rseq_cs as is so that rseq_sycall() will detect and
>>>>> @@ -419,9 +428,11 @@ void __rseq_handle_notify_resume(struct ksignal *ksig, struct pt_regs *regs)
>>>>>         }
>>>>>         if (unlikely(rseq_update_cpu_node_id(t)))
>>>>>                 goto error;
>>>>> +     write_pkey_val(saved_pkey);
>>>>>         return;
>>>>>
>>>>>     error:
>>>>> +     write_pkey_val(saved_pkey);
>>>>>         sig = ksig ? ksig->sig : 0;
>>>>>         force_sigsegv(sig);
>>>>>     }


-- 
Mathieu Desnoyers
EfficiOS Inc.
https://www.efficios.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 3/4] rseq: Make rseq work with protection keys
  2025-02-25 14:53           ` Mathieu Desnoyers
@ 2025-02-27 14:03             ` Dmitry Vyukov
  0 siblings, 0 replies; 16+ messages in thread
From: Dmitry Vyukov @ 2025-02-27 14:03 UTC (permalink / raw)
  To: Mathieu Desnoyers
  Cc: peterz, boqun.feng, tglx, mingo, bp, dave.hansen, hpa,
	aruna.ramakrishna, elver, Paul E. McKenney, x86, linux-kernel

On Tue, 25 Feb 2025 at 15:53, Mathieu Desnoyers
<mathieu.desnoyers@efficios.com> wrote:
>
> On 2025-02-25 09:51, Dmitry Vyukov wrote:
> > On Tue, 25 Feb 2025 at 15:28, Mathieu Desnoyers
> > <mathieu.desnoyers@efficios.com> wrote:
> >>
> >> On 2025-02-25 09:07, Dmitry Vyukov wrote:
> >>> On Mon, 24 Feb 2025 at 20:18, Mathieu Desnoyers
> >>> <mathieu.desnoyers@efficios.com> wrote:
> >>>>
> >>>> On 2025-02-24 08:20, Dmitry Vyukov wrote:
> >>>>> If an application registers rseq, and ever switches to another pkey
> >>>>> protection (such that the rseq becomes inaccessible), then any
> >>>>> context switch will cause failure in __rseq_handle_notify_resume()
> >>>>> attempting to read/write struct rseq and/or rseq_cs. Since context
> >>>>> switches are asynchronous and are outside of the application control
> >>>>> (not part of the restricted code scope), temporarily switch to
> >>>>> pkey value that allows access to the 0 (default) PKEY.
> >>>>
> >>>> This is a good start, but the plan Dave and I discussed went further
> >>>> than this. Those additions are needed:
> >>>>
> >>>> 1) Add validation at rseq registration that the struct rseq is indeed
> >>>>       pkey-0 memory (return failure if not).
> >>>
> >>> I don't think this is worth it for multiple reasons:
> >>>    - a program may first register it and then assign a key, which means
> >>> we also need to check in pkey_mprotect
> >>>    - pkey_mprotect may be applied to rseq of another thread, so ensuring
> >>> that will require complex code with non-trivial synchronization and
> >>> will add considerable overhead to pkey_mprotect call
> >>>    - a program may assign non-0 pkey but have it always accessible, such
> >>> programs will break by the new check
> >>>    - the misuse is already detected by rseq code, and UNIX errno-based
> >>> reporting is not very informative and does not add much value on top
> >>> of existing reporting
> >>>    - this is not different from registering rseq and then unmap'ing the
> >>> memory, checking that does not look like a good idea, and checking
> >>> only subset of misuses is inconsistent
> >>>
> >>> Based on my experience with rseq, what would be useful is reporting a
> >>> meaningful siginfo for access errors (address/unique code) and fixing
> >>> signal delivery. That would solve all of the above problems, and
> >>> provide useful info for the user (not just confusing EINVAL from
> >>> mprotect/munmap).
> >>>
> >>> But I would prefer to not mix these unrelated usability improvements
> >>> and bug fixes with this change. That's not related to this change.
> >>
> >> I agree with your arguments. If Dave is OK with it, I'd be fine with
> >> leaving out the pkey-0 validation on rseq registration, and eventually
> >> bring meaningful siginfo access errors as future improvements.
> >>
> >> So the new behavior would be that both rseq and rseq_cs are required
> >> to be pkey-0. If they are not and their pkey is not accessible in the
> >> current context, it would trigger a segmentation fault. Ideally we'd
> >> want to document this somewhere in the UAPI header.
> >
> > Makes sense. I will wait for Dave comments/ack before sending v6. But
> > to save a round-trip, does this look reasonable?
> >
> > --- a/include/uapi/linux/rseq.h
> > +++ b/include/uapi/linux/rseq.h
> > @@ -58,6 +58,10 @@ struct rseq_cs {
> >    * contained within a single cache-line.
> >    *
> >    * A single struct rseq per thread is allowed.
> > + *
> > + * If struct rseq or struct rseq_cs is used with Memory Protection Keys,
> > + * then the assigned pkey should either be accessible whenever these structs
> > + * are registered/installed, or they should be protected with pkey 0.
>
> The wording is OK with me.
>
> Thanks,
>
> Mathieu

Sent v6 with this documentation change.

> >    */
> >   struct rseq {
> >
> >
> >
> >>>> 2) The pkey-0 requirement is only for struct rseq, which we can check
> >>>>       for at rseq registration, and happens to be the fast path. For struct
> >>>>       rseq_cs, this is not the same tradeoff: we cannot easily check its
> >>>>       associated pkey because the rseq_cs pointer is updated by userspace
> >>>>       when entering a critical section. But the good news is that reading
> >>>>       the content of struct rseq_cs is *not* a fast-path: it's only done
> >>>>       when preempting/delivering a signal over a thread which has a
> >>>>       non-NULL rseq_cs pointer.
> >>>
> >>> rseq_cs is usually accessed on a hot path since rseq_cs pointer is not
> >>> cleared on critical section exit (at least that's what we do).
> >>
> >> Fair point.
> >>
> >>>
> >>>>       Therefore reading the struct rseq_cs content should be done with
> >>>>       write_permissive_pkey_val(), giving access to all pkeys.
> >>>
> >>> You just asked me to redo the code to simplify it, won't this
> >>> complicate it back again? ;)
> >>
> >> I'm fine with the pkey-0 approach for both rseq and rseq_cs if Dave is
> >> also OK with it.
> >
> > It should work for my current use case, at least how I currently see
> > it. Ways people use pkeys are pretty unique, so it's hard to
> > extrapolate. But there is one more possibility: when a program
> > switches PKEYs, it may also clear stale rseq_cs pointer from rseq.
> > This way rseq_cs may have non-0 keys assigned, but they are always
> > accessible while installed.
> >
> >
> >
> >> Thanks,
> >>
> >> Mathieu
> >>
> >>>
> >>>
> >>>> Thanks,
> >>>>
> >>>> Mathieu
> >>>>
> >>>>>
> >>>>> Signed-off-by: Dmitry Vyukov <dvyukov@google.com>
> >>>>> Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
> >>>>> Cc: Peter Zijlstra <peterz@infradead.org>
> >>>>> Cc: "Paul E. McKenney" <paulmck@kernel.org>
> >>>>> Cc: Boqun Feng <boqun.feng@gmail.com>
> >>>>> Cc: Thomas Gleixner <tglx@linutronix.de>
> >>>>> Cc: Ingo Molnar <mingo@redhat.com>
> >>>>> Cc: Borislav Petkov <bp@alien8.de>
> >>>>> Cc: Dave Hansen <dave.hansen@linux.intel.com>
> >>>>> Cc: "H. Peter Anvin" <hpa@zytor.com>
> >>>>> Cc: Aruna Ramakrishna <aruna.ramakrishna@oracle.com>
> >>>>> Cc: x86@kernel.org
> >>>>> Cc: linux-kernel@vger.kernel.org
> >>>>> Fixes: d7822b1e24f2 ("rseq: Introduce restartable sequences system call")
> >>>>>
> >>>>> ---
> >>>>> Changes in v4:
> >>>>>     - Added Fixes tag
> >>>>>
> >>>>> Changes in v3:
> >>>>>     - simplify control flow to always enable access to 0 pkey
> >>>>>
> >>>>> Changes in v2:
> >>>>>     - fixed typos and reworded the comment
> >>>>> ---
> >>>>>     kernel/rseq.c | 11 +++++++++++
> >>>>>     1 file changed, 11 insertions(+)
> >>>>>
> >>>>> diff --git a/kernel/rseq.c b/kernel/rseq.c
> >>>>> index 2cb16091ec0ae..9d9c976d3b78c 100644
> >>>>> --- a/kernel/rseq.c
> >>>>> +++ b/kernel/rseq.c
> >>>>> @@ -10,6 +10,7 @@
> >>>>>
> >>>>>     #include <linux/sched.h>
> >>>>>     #include <linux/uaccess.h>
> >>>>> +#include <linux/pkeys.h>
> >>>>>     #include <linux/syscalls.h>
> >>>>>     #include <linux/rseq.h>
> >>>>>     #include <linux/types.h>
> >>>>> @@ -402,11 +403,19 @@ static int rseq_ip_fixup(struct pt_regs *regs)
> >>>>>     void __rseq_handle_notify_resume(struct ksignal *ksig, struct pt_regs *regs)
> >>>>>     {
> >>>>>         struct task_struct *t = current;
> >>>>> +     pkey_reg_t saved_pkey;
> >>>>>         int ret, sig;
> >>>>>
> >>>>>         if (unlikely(t->flags & PF_EXITING))
> >>>>>                 return;
> >>>>>
> >>>>> +     /*
> >>>>> +      * Enable access to the default (0) pkey in case the thread has
> >>>>> +      * currently disabled access to it and struct rseq/rseq_cs has
> >>>>> +      * 0 pkey assigned (the only supported value for now).
> >>>>> +      */
> >>>>> +     saved_pkey = enable_zero_pkey_val();
> >>>>> +
> >>>>>         /*
> >>>>>          * regs is NULL if and only if the caller is in a syscall path.  Skip
> >>>>>          * fixup and leave rseq_cs as is so that rseq_sycall() will detect and
> >>>>> @@ -419,9 +428,11 @@ void __rseq_handle_notify_resume(struct ksignal *ksig, struct pt_regs *regs)
> >>>>>         }
> >>>>>         if (unlikely(rseq_update_cpu_node_id(t)))
> >>>>>                 goto error;
> >>>>> +     write_pkey_val(saved_pkey);
> >>>>>         return;
> >>>>>
> >>>>>     error:
> >>>>> +     write_pkey_val(saved_pkey);
> >>>>>         sig = ksig ? ksig->sig : 0;
> >>>>>         force_sigsegv(sig);
> >>>>>     }
>
>
> --
> Mathieu Desnoyers
> EfficiOS Inc.
> https://www.efficios.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2025-02-27 14:04 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <cover.1740403209.git.dvyukov@google.com>
2025-02-24 13:20 ` [PATCH v4 1/4] pkeys: add API to switch to permissive/zero pkey register Dmitry Vyukov
2025-02-24 19:04   ` Mathieu Desnoyers
2025-02-25 13:54     ` Dmitry Vyukov
2025-02-24 13:20 ` [PATCH v4 2/4] x86/signal: Use write_permissive_pkey_val() helper Dmitry Vyukov
2025-02-24 19:11   ` Mathieu Desnoyers
2025-02-24 13:20 ` [PATCH v4 3/4] rseq: Make rseq work with protection keys Dmitry Vyukov
2025-02-24 19:18   ` Mathieu Desnoyers
2025-02-25 14:07     ` Dmitry Vyukov
2025-02-25 14:28       ` Mathieu Desnoyers
2025-02-25 14:51         ` Dmitry Vyukov
2025-02-25 14:53           ` Mathieu Desnoyers
2025-02-27 14:03             ` Dmitry Vyukov
2025-02-24 13:20 ` [PATCH v4 4/4] selftests/rseq: Add test for rseq+pkeys Dmitry Vyukov
2025-02-24 19:48   ` Mathieu Desnoyers
2025-02-25 13:55     ` Dmitry Vyukov
2025-02-24 13:28 ` [PATCH v4 0/4] rseq: Make rseq work with protection keys Dmitry Vyukov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox