Linux Trace Kernel
 help / color / mirror / Atom feed
* [PATCH v2 1/2] x86/uprobes: Keep shadow stack in sync for emulated CALLs
@ 2026-06-30  0:13 David Windsor
  2026-06-30  0:13 ` [PATCH v2 2/2] selftests/x86: Add shadow stack uprobe CALL test David Windsor
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: David Windsor @ 2026-06-30  0:13 UTC (permalink / raw)
  To: mhiramat, oleg, peterz
  Cc: tglx, mingo, bp, dave.hansen, x86, shuah, rick.p.edgecombe, jolsa,
	linux-trace-kernel, linux-kselftest, linux-kernel, David Windsor

Uprobe CALL emulation updates the normal user stack, but not the CET user
shadow stack. The subsequent RET then sees a stale shadow stack entry and
raises #CP.

Update the relative CALL emulation and XOL CALL fixup paths to keep the
shadow stack in sync.

Fixes: 488af8ea7131 ("x86/shstk: Wire in shadow stack interface")
Signed-off-by: David Windsor <dwindsor@gmail.com>
---

v2:
 - propagate error from shshk_update_last_frame() rather than returning
   -ERESTART in default_post_xol_op(). (Oleg)

v1: https://lore.kernel.org/all/20260622183109.1137245-1-dwindsor@gmail.com/

 arch/x86/kernel/uprobes.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c
index ebb1baf1eb1d..d74bb54543b6 100644
--- a/arch/x86/kernel/uprobes.c
+++ b/arch/x86/kernel/uprobes.c
@@ -1246,9 +1246,15 @@ static int default_post_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs
 		long correction = utask->vaddr - utask->xol_vaddr;
 		regs->ip += correction;
 	} else if (auprobe->defparam.fixups & UPROBE_FIX_CALL) {
+		unsigned long retaddr = utask->vaddr + auprobe->defparam.ilen;
+		int err;
+
 		regs->sp += sizeof_long(regs); /* Pop incorrect return address */
-		if (emulate_push_stack(regs, utask->vaddr + auprobe->defparam.ilen))
+		if (emulate_push_stack(regs, retaddr))
 			return -ERESTART;
+		err = shstk_update_last_frame(retaddr);
+		if (err)
+			return err;
 	}
 	/* popf; tell the caller to not touch TF */
 	if (auprobe->defparam.fixups & UPROBE_FIX_SETF)
@@ -1338,6 +1344,10 @@ static bool branch_emulate_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
 		 */
 		if (emulate_push_stack(regs, new_ip))
 			return false;
+		if (shstk_push(new_ip) == -EFAULT) {
+			regs->sp += sizeof_long(regs);
+			return false;
+		}
 	} else if (!check_jmp_cond(auprobe, regs)) {
 		offs = 0;
 	}
-- 
2.53.0


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

* [PATCH v2 2/2] selftests/x86: Add shadow stack uprobe CALL test
  2026-06-30  0:13 [PATCH v2 1/2] x86/uprobes: Keep shadow stack in sync for emulated CALLs David Windsor
@ 2026-06-30  0:13 ` David Windsor
  2026-06-30  8:39 ` [PATCH v2 1/2] x86/uprobes: Keep shadow stack in sync for emulated CALLs Oleg Nesterov
  2026-06-30 11:59 ` Jiri Olsa
  2 siblings, 0 replies; 4+ messages in thread
From: David Windsor @ 2026-06-30  0:13 UTC (permalink / raw)
  To: mhiramat, oleg, peterz
  Cc: tglx, mingo, bp, dave.hansen, x86, shuah, rick.p.edgecombe, jolsa,
	linux-trace-kernel, linux-kselftest, linux-kernel, David Windsor

Add coverage for entry uprobes installed on CALL instructions while user
shadow stack is enabled. The test puts an entry uprobe on a helper whose
first instruction is a relative CALL, then verifies that the call/return
sequence completes without SIGSEGV.

This catches regressions where x86 uprobe CALL emulation updates the
regular user stack but leaves the CET shadow stack stale.

Signed-off-by: David Windsor <dwindsor@gmail.com>
---

Notes:
    v2:
      - New patch. Adds a uprobe-on-CALL subtest to test_shadow_stack_64 to
        cover the fix in 1/2. Verifies that an emulated CALL through a
        uprobe leaves the shadow stack consistent with the user stack
        (no #CP on the matching RET).

 .../testing/selftests/x86/test_shadow_stack.c | 86 +++++++++++++++++++
 1 file changed, 86 insertions(+)

diff --git a/tools/testing/selftests/x86/test_shadow_stack.c b/tools/testing/selftests/x86/test_shadow_stack.c
index 21af54d5f4ea..3d6ca33edba4 100644
--- a/tools/testing/selftests/x86/test_shadow_stack.c
+++ b/tools/testing/selftests/x86/test_shadow_stack.c
@@ -873,6 +873,86 @@ static int test_uretprobe(void)
 	return err;
 }
 
+/* Keep the CALL first so the function address is exactly the probed CALL. */
+extern void uprobe_call_trigger(void);
+asm (".pushsection .text\n"
+	".global uprobe_call_target\n"
+	".type uprobe_call_target, @function\n"
+	"uprobe_call_target:\n"
+	"	ret\n"
+	".size uprobe_call_target, .-uprobe_call_target\n"
+
+	".global uprobe_call_trigger\n"
+	".type uprobe_call_trigger, @function\n"
+	"uprobe_call_trigger:\n"
+	"	call uprobe_call_target\n"
+	"	ret\n"
+	".size uprobe_call_trigger, .-uprobe_call_trigger\n"
+	".popsection\n"
+);
+
+/* If CALL emulation misses the shadow stack update, this exits via SIGSEGV. */
+static int test_uprobe_call(void)
+{
+	const size_t attr_sz = sizeof(struct perf_event_attr);
+	const char *file = "/proc/self/exe";
+	int fd = -1, type, err = 1;
+	struct perf_event_attr attr;
+	struct sigaction sa = {};
+	ssize_t offset;
+
+	type = determine_uprobe_perf_type();
+	if (type < 0) {
+		if (type == -ENOENT)
+			printf("[SKIP]\tUprobe on CALL test, uprobes are not available\n");
+		return 0;
+	}
+
+	offset = get_uprobe_offset(uprobe_call_trigger);
+	if (offset < 0)
+		return 1;
+
+	sa.sa_sigaction = segv_gp_handler;
+	sa.sa_flags = SA_SIGINFO;
+	if (sigaction(SIGSEGV, &sa, NULL))
+		return 1;
+
+	/* Setup entry uprobe through perf event interface. */
+	memset(&attr, 0, attr_sz);
+	attr.size = attr_sz;
+	attr.type = type;
+	attr.config = 0;
+	attr.config1 = (__u64)(unsigned long)file;
+	attr.config2 = offset;
+
+	fd = syscall(__NR_perf_event_open, &attr, 0 /* pid */, -1 /* cpu */,
+		     -1 /* group_fd */, PERF_FLAG_FD_CLOEXEC);
+	if (fd < 0)
+		goto out;
+
+	if (sigsetjmp(jmp_buffer, 1))
+		goto out;
+
+	if (ARCH_PRCTL(ARCH_SHSTK_ENABLE, ARCH_SHSTK_SHSTK))
+		goto out;
+
+	/*
+	 * This either segfaults and goes through sigsetjmp above
+	 * or succeeds and we're good.
+	 */
+	uprobe_call_trigger();
+
+	printf("[OK]\tUprobe on CALL test\n");
+	err = 0;
+
+out:
+	ARCH_PRCTL(ARCH_SHSTK_DISABLE, ARCH_SHSTK_SHSTK);
+	signal(SIGSEGV, SIG_DFL);
+	if (fd >= 0)
+		close(fd);
+	return err;
+}
+
 void segv_handler_ptrace(int signum, siginfo_t *si, void *uc)
 {
 	/* The SSP adjustment caused a segfault. */
@@ -1071,6 +1151,12 @@ int main(int argc, char *argv[])
 		goto out;
 	}
 
+	if (test_uprobe_call()) {
+		ret = 1;
+		printf("[FAIL]\tuprobe on CALL test\n");
+		goto out;
+	}
+
 	return ret;
 
 out:
-- 
2.53.0


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

* Re: [PATCH v2 1/2] x86/uprobes: Keep shadow stack in sync for emulated CALLs
  2026-06-30  0:13 [PATCH v2 1/2] x86/uprobes: Keep shadow stack in sync for emulated CALLs David Windsor
  2026-06-30  0:13 ` [PATCH v2 2/2] selftests/x86: Add shadow stack uprobe CALL test David Windsor
@ 2026-06-30  8:39 ` Oleg Nesterov
  2026-06-30 11:59 ` Jiri Olsa
  2 siblings, 0 replies; 4+ messages in thread
From: Oleg Nesterov @ 2026-06-30  8:39 UTC (permalink / raw)
  To: David Windsor
  Cc: mhiramat, peterz, tglx, mingo, bp, dave.hansen, x86, shuah,
	rick.p.edgecombe, jolsa, linux-trace-kernel, linux-kselftest,
	linux-kernel

On 06/29, David Windsor wrote:
>
> Uprobe CALL emulation updates the normal user stack, but not the CET user
> shadow stack. The subsequent RET then sees a stale shadow stack entry and
> raises #CP.
>
> Update the relative CALL emulation and XOL CALL fixup paths to keep the
> shadow stack in sync.
>
> Fixes: 488af8ea7131 ("x86/shstk: Wire in shadow stack interface")
> Signed-off-by: David Windsor <dwindsor@gmail.com>

Acked-by: Oleg Nesterov <oleg@redhat.com>


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

* Re: [PATCH v2 1/2] x86/uprobes: Keep shadow stack in sync for emulated CALLs
  2026-06-30  0:13 [PATCH v2 1/2] x86/uprobes: Keep shadow stack in sync for emulated CALLs David Windsor
  2026-06-30  0:13 ` [PATCH v2 2/2] selftests/x86: Add shadow stack uprobe CALL test David Windsor
  2026-06-30  8:39 ` [PATCH v2 1/2] x86/uprobes: Keep shadow stack in sync for emulated CALLs Oleg Nesterov
@ 2026-06-30 11:59 ` Jiri Olsa
  2 siblings, 0 replies; 4+ messages in thread
From: Jiri Olsa @ 2026-06-30 11:59 UTC (permalink / raw)
  To: David Windsor
  Cc: mhiramat, oleg, peterz, tglx, mingo, bp, dave.hansen, x86, shuah,
	rick.p.edgecombe, linux-trace-kernel, linux-kselftest,
	linux-kernel

On Mon, Jun 29, 2026 at 08:13:33PM -0400, David Windsor wrote:
> Uprobe CALL emulation updates the normal user stack, but not the CET user
> shadow stack. The subsequent RET then sees a stale shadow stack entry and
> raises #CP.
> 
> Update the relative CALL emulation and XOL CALL fixup paths to keep the
> shadow stack in sync.
> 
> Fixes: 488af8ea7131 ("x86/shstk: Wire in shadow stack interface")
> Signed-off-by: David Windsor <dwindsor@gmail.com>

hi, lgtm

Tested-by: Jiri Olsa <jolsa@kernel.org>
Acked-by: Jiri Olsa <jolsa@kernel.org>

jirka


> ---
> 
> v2:
>  - propagate error from shshk_update_last_frame() rather than returning
>    -ERESTART in default_post_xol_op(). (Oleg)
> 
> v1: https://lore.kernel.org/all/20260622183109.1137245-1-dwindsor@gmail.com/
> 
>  arch/x86/kernel/uprobes.c | 12 +++++++++++-
>  1 file changed, 11 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c
> index ebb1baf1eb1d..d74bb54543b6 100644
> --- a/arch/x86/kernel/uprobes.c
> +++ b/arch/x86/kernel/uprobes.c
> @@ -1246,9 +1246,15 @@ static int default_post_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs
>  		long correction = utask->vaddr - utask->xol_vaddr;
>  		regs->ip += correction;
>  	} else if (auprobe->defparam.fixups & UPROBE_FIX_CALL) {
> +		unsigned long retaddr = utask->vaddr + auprobe->defparam.ilen;
> +		int err;
> +
>  		regs->sp += sizeof_long(regs); /* Pop incorrect return address */
> -		if (emulate_push_stack(regs, utask->vaddr + auprobe->defparam.ilen))
> +		if (emulate_push_stack(regs, retaddr))
>  			return -ERESTART;
> +		err = shstk_update_last_frame(retaddr);
> +		if (err)
> +			return err;
>  	}
>  	/* popf; tell the caller to not touch TF */
>  	if (auprobe->defparam.fixups & UPROBE_FIX_SETF)
> @@ -1338,6 +1344,10 @@ static bool branch_emulate_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
>  		 */
>  		if (emulate_push_stack(regs, new_ip))
>  			return false;
> +		if (shstk_push(new_ip) == -EFAULT) {
> +			regs->sp += sizeof_long(regs);
> +			return false;
> +		}
>  	} else if (!check_jmp_cond(auprobe, regs)) {
>  		offs = 0;
>  	}
> -- 
> 2.53.0
> 

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

end of thread, other threads:[~2026-06-30 11:59 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-30  0:13 [PATCH v2 1/2] x86/uprobes: Keep shadow stack in sync for emulated CALLs David Windsor
2026-06-30  0:13 ` [PATCH v2 2/2] selftests/x86: Add shadow stack uprobe CALL test David Windsor
2026-06-30  8:39 ` [PATCH v2 1/2] x86/uprobes: Keep shadow stack in sync for emulated CALLs Oleg Nesterov
2026-06-30 11:59 ` Jiri Olsa

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