* [PATCH 1/2] x86/uprobes: Keep shadow stack in sync for emulated CALLs
@ 2026-06-22 18:31 David Windsor
2026-06-22 18:31 ` [PATCH 2/2] selftests/x86: Add shadow stack uprobe CALL test David Windsor
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: David Windsor @ 2026-06-22 18:31 UTC (permalink / raw)
To: mhiramat, oleg, peterz
Cc: tglx, mingo, bp, dave.hansen, x86, shuah, 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>
---
arch/x86/kernel/uprobes.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c
index ebb1baf1eb1d..ae32013a7097 100644
--- a/arch/x86/kernel/uprobes.c
+++ b/arch/x86/kernel/uprobes.c
@@ -1246,8 +1246,12 @@ 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;
+
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;
+ if (shstk_update_last_frame(retaddr))
return -ERESTART;
}
/* popf; tell the caller to not touch TF */
@@ -1338,6 +1342,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.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 2/2] selftests/x86: Add shadow stack uprobe CALL test
2026-06-22 18:31 [PATCH 1/2] x86/uprobes: Keep shadow stack in sync for emulated CALLs David Windsor
@ 2026-06-22 18:31 ` David Windsor
2026-06-23 8:43 ` [PATCH 1/2] x86/uprobes: Keep shadow stack in sync for emulated CALLs Peter Zijlstra
2026-06-23 12:52 ` Oleg Nesterov
2 siblings, 0 replies; 7+ messages in thread
From: David Windsor @ 2026-06-22 18:31 UTC (permalink / raw)
To: mhiramat, oleg, peterz
Cc: tglx, mingo, bp, dave.hansen, x86, shuah, 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>
---
tools/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.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH 1/2] x86/uprobes: Keep shadow stack in sync for emulated CALLs
2026-06-22 18:31 [PATCH 1/2] x86/uprobes: Keep shadow stack in sync for emulated CALLs David Windsor
2026-06-22 18:31 ` [PATCH 2/2] selftests/x86: Add shadow stack uprobe CALL test David Windsor
@ 2026-06-23 8:43 ` Peter Zijlstra
2026-06-23 14:07 ` David Windsor
2026-06-23 12:52 ` Oleg Nesterov
2 siblings, 1 reply; 7+ messages in thread
From: Peter Zijlstra @ 2026-06-23 8:43 UTC (permalink / raw)
To: David Windsor
Cc: mhiramat, oleg, tglx, mingo, bp, dave.hansen, x86, shuah,
linux-trace-kernel, linux-kselftest, linux-kernel
On Mon, Jun 22, 2026 at 02:31:08PM -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")
I can confirm this patch fixes the included test case, so yay for that.
However, should this not be:
Fixes: 1713b63a07a2 ("x86/shstk: Make return uprobe work with shadow stack")
?
> Signed-off-by: David Windsor <dwindsor@gmail.com>
> ---
> arch/x86/kernel/uprobes.c | 10 +++++++++-
> 1 file changed, 9 insertions(+), 1 deletion(-)
>
> diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c
> index ebb1baf1eb1d..ae32013a7097 100644
> --- a/arch/x86/kernel/uprobes.c
> +++ b/arch/x86/kernel/uprobes.c
> @@ -1246,8 +1246,12 @@ 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;
> +
> 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;
> + if (shstk_update_last_frame(retaddr))
> return -ERESTART;
> }
> /* popf; tell the caller to not touch TF */
> @@ -1338,6 +1342,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.43.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 1/2] x86/uprobes: Keep shadow stack in sync for emulated CALLs
2026-06-22 18:31 [PATCH 1/2] x86/uprobes: Keep shadow stack in sync for emulated CALLs David Windsor
2026-06-22 18:31 ` [PATCH 2/2] selftests/x86: Add shadow stack uprobe CALL test David Windsor
2026-06-23 8:43 ` [PATCH 1/2] x86/uprobes: Keep shadow stack in sync for emulated CALLs Peter Zijlstra
@ 2026-06-23 12:52 ` Oleg Nesterov
2026-06-23 12:57 ` Peter Zijlstra
2 siblings, 1 reply; 7+ messages in thread
From: Oleg Nesterov @ 2026-06-23 12:52 UTC (permalink / raw)
To: David Windsor
Cc: mhiramat, peterz, tglx, mingo, bp, dave.hansen, x86, shuah,
linux-trace-kernel, linux-kselftest, linux-kernel
On 06/22, David Windsor wrote:
>
> --- a/arch/x86/kernel/uprobes.c
> +++ b/arch/x86/kernel/uprobes.c
> @@ -1246,8 +1246,12 @@ 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;
> +
> 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;
> + if (shstk_update_last_frame(retaddr))
> return -ERESTART;
Well, if shstk_update_last_frame() fails after emulate_push_stack(), we should
probably return another error, so that the caller handle_singlestep() will kill
this task?
Oleg.
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 1/2] x86/uprobes: Keep shadow stack in sync for emulated CALLs
2026-06-23 12:52 ` Oleg Nesterov
@ 2026-06-23 12:57 ` Peter Zijlstra
2026-06-23 13:25 ` Oleg Nesterov
0 siblings, 1 reply; 7+ messages in thread
From: Peter Zijlstra @ 2026-06-23 12:57 UTC (permalink / raw)
To: Oleg Nesterov
Cc: David Windsor, mhiramat, tglx, mingo, bp, dave.hansen, x86, shuah,
linux-trace-kernel, linux-kselftest, linux-kernel
On Tue, Jun 23, 2026 at 02:52:32PM +0200, Oleg Nesterov wrote:
> On 06/22, David Windsor wrote:
> >
> > --- a/arch/x86/kernel/uprobes.c
> > +++ b/arch/x86/kernel/uprobes.c
> > @@ -1246,8 +1246,12 @@ 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;
> > +
> > 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;
> > + if (shstk_update_last_frame(retaddr))
> > return -ERESTART;
>
> Well, if shstk_update_last_frame() fails after emulate_push_stack(), we should
> probably return another error, so that the caller handle_singlestep() will kill
> this task?
Makes sense, the other user has a force_sig(SIGSEGV) on failure.
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 1/2] x86/uprobes: Keep shadow stack in sync for emulated CALLs
2026-06-23 12:57 ` Peter Zijlstra
@ 2026-06-23 13:25 ` Oleg Nesterov
0 siblings, 0 replies; 7+ messages in thread
From: Oleg Nesterov @ 2026-06-23 13:25 UTC (permalink / raw)
To: Peter Zijlstra
Cc: David Windsor, mhiramat, tglx, mingo, bp, dave.hansen, x86, shuah,
linux-trace-kernel, linux-kselftest, linux-kernel
On 06/23, Peter Zijlstra wrote:
>
> On Tue, Jun 23, 2026 at 02:52:32PM +0200, Oleg Nesterov wrote:
> > On 06/22, David Windsor wrote:
> > >
> > > --- a/arch/x86/kernel/uprobes.c
> > > +++ b/arch/x86/kernel/uprobes.c
> > > @@ -1246,8 +1246,12 @@ 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;
> > > +
> > > 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;
> > > + if (shstk_update_last_frame(retaddr))
> > > return -ERESTART;
> >
> > Well, if shstk_update_last_frame() fails after emulate_push_stack(), we should
> > probably return another error, so that the caller handle_singlestep() will kill
> > this task?
>
> Makes sense, the other user has a force_sig(SIGSEGV) on failure.
Offtopic question... both shstk_update_last_frame() and shstk_push() are only
used by arch/x86/kernel/uprobes.c. But they are not symmetric in that
shstk_update_last_frame() returns 0 if !features_enabled(ARCH_SHSTK_SHSTK),
while shstk_push() returns -ENOTSUPP in this case.
That is why the users can't just do "if (shstk_push(xxx)) ...". This is really
minor, but perhaps it makes sense to change shstk_push() to return 0 in this
case too? I don't think -ENOTSUPP is actually useful...
Oleg.
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 1/2] x86/uprobes: Keep shadow stack in sync for emulated CALLs
2026-06-23 8:43 ` [PATCH 1/2] x86/uprobes: Keep shadow stack in sync for emulated CALLs Peter Zijlstra
@ 2026-06-23 14:07 ` David Windsor
0 siblings, 0 replies; 7+ messages in thread
From: David Windsor @ 2026-06-23 14:07 UTC (permalink / raw)
To: Peter Zijlstra
Cc: mhiramat, oleg, tglx, mingo, bp, dave.hansen, x86, shuah,
linux-trace-kernel, linux-kselftest, linux-kernel
On Tue, Jun 23, 2026 at 4:43 AM Peter Zijlstra <peterz@infradead.org> wrote:
>
> On Mon, Jun 22, 2026 at 02:31:08PM -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")
>
> I can confirm this patch fixes the included test case, so yay for that.
>
> However, should this not be:
>
> Fixes: 1713b63a07a2 ("x86/shstk: Make return uprobe work with shadow stack")
>
> ?
>
Hmm, this commit appears to only be concerned with the uretprobe case?
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-06-23 14:07 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-22 18:31 [PATCH 1/2] x86/uprobes: Keep shadow stack in sync for emulated CALLs David Windsor
2026-06-22 18:31 ` [PATCH 2/2] selftests/x86: Add shadow stack uprobe CALL test David Windsor
2026-06-23 8:43 ` [PATCH 1/2] x86/uprobes: Keep shadow stack in sync for emulated CALLs Peter Zijlstra
2026-06-23 14:07 ` David Windsor
2026-06-23 12:52 ` Oleg Nesterov
2026-06-23 12:57 ` Peter Zijlstra
2026-06-23 13:25 ` Oleg Nesterov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox