* [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