From: Richard Patel <ripatel@wii.dev>
To: Rick Edgecombe <rick.p.edgecombe@intel.com>,
Thomas Gleixner <tglx@kernel.org>, Ingo Molnar <mingo@redhat.com>,
Borislav Petkov <bp@alien8.de>,
Dave Hansen <dave.hansen@linux.intel.com>,
x86@kernel.org
Cc: "H . Peter Anvin" <hpa@zytor.com>, Kees Cook <kees@kernel.org>,
Shuah Khan <shuah@kernel.org>,
linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org,
Richard Patel <ripatel@wii.dev>
Subject: [PATCH 2/3] selftests/x86: test shadow stack sigreturn protection
Date: Thu, 8 Oct 2026 20:16:09 +0000 [thread overview]
Message-ID: <20261008201610.1003569-3-ripatel@wii.dev> (raw)
In-Reply-To: <20261008201610.1003569-1-ripatel@wii.dev>
Check that a crafted signal frame return address (rip) is rejected
by rt_sigreturn when shadow stack is enabled. Test both 'syscall'
and 'int $0x80' (CONFIG_IA32_EMULATION) with and without SHSTK.
Signed-off-by: Richard Patel <ripatel@wii.dev>
---
.../testing/selftests/x86/test_shadow_stack.c | 110 ++++++++++++++++++
1 file changed, 110 insertions(+)
diff --git a/tools/testing/selftests/x86/test_shadow_stack.c b/tools/testing/selftests/x86/test_shadow_stack.c
index 3d6ca33edba4..67baebbdbd87 100644
--- a/tools/testing/selftests/x86/test_shadow_stack.c
+++ b/tools/testing/selftests/x86/test_shadow_stack.c
@@ -735,6 +735,110 @@ int test_32bit(void)
return !segv_triggered;
}
+/*
+ * Fork and sigreturn with a crafted signal frame.
+ * Returns the child's exit code, or 0x100+signal if it was killed.
+ */
+static int crafted_sigreturn(unsigned long sp, bool ia32, bool shstk)
+{
+ int status;
+ pid_t pid;
+
+ pid = fork();
+ if (!pid) {
+ signal(SIGSEGV, SIG_DFL);
+ if (ARCH_PRCTL(shstk ? ARCH_SHSTK_ENABLE : ARCH_SHSTK_DISABLE,
+ ARCH_SHSTK_SHSTK))
+ _exit(1);
+ if (ia32) /* ia32 rt_sigreturn */
+ asm volatile("movq %0, %%rsp; int $0x80; ud2"
+ : : "r" (sp), "a" (173));
+ else /* rt_sigreturn */
+ asm volatile("movq %0, %%rsp; syscall; ud2"
+ : : "r" (sp), "a" (__NR_rt_sigreturn));
+ __builtin_unreachable();
+ }
+
+ if (pid < 0 || waitpid(pid, &status, 0) != pid)
+ return -1;
+ return WIFSIGNALED(status) ? 0x100 + WTERMSIG(status) : WEXITSTATUS(status);
+}
+
+struct rt_sigframe_ia32 {
+ uint32_t pretcode, sig, pinfo, puc;
+ uint8_t info[128];
+ uint32_t uc_flags, uc_link, ss_sp, ss_flags, ss_size;
+ uint16_t gs, __gsh, fs, __fsh, es, __esh, ds, __dsh;
+ uint32_t di, si, bp, sp, bx, dx, cx, ax, trapno, err, ip;
+ uint16_t cs, __csh;
+ uint32_t flags, sp_at_signal;
+ uint16_t ss, __ssh;
+ uint32_t fpstate, oldmask, cr2;
+ uint32_t uc_sigmask[2];
+ uint8_t retcode[8];
+};
+
+_Static_assert(sizeof(struct rt_sigframe_ia32) == 268, "ia32 rt_sigframe layout");
+
+/* This tests whether shadow stack protects sigreturn */
+int test_sigreturn(void)
+{
+ static const uint8_t target_routine[] = {
+ 0xbf, 0x2a, 0x00, 0x00, 0x00, /* mov $42, %edi */
+ 0xb8, 0xe7, 0x00, 0x00, 0x00, /* mov $231, %eax (exit_group) */
+ 0x0f, 0x05, /* syscall */
+ };
+
+ struct { uint64_t pretcode; ucontext_t uc; } *f64;
+ struct rt_sigframe_ia32 *f32;
+ void *retsite;
+ int ret = 1;
+
+ /* ia32 sigreturn truncates RIP and RSP to 32 bits */
+ retsite = mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_32BIT | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (retsite == MAP_FAILED)
+ return 1;
+ memcpy(retsite, target_routine, sizeof(target_routine));
+
+ f64 = retsite + 0x100;
+ f64->uc.uc_mcontext.gregs[REG_RIP] = (unsigned long)retsite;
+ f64->uc.uc_mcontext.gregs[REG_RSP] = (unsigned long)retsite + PAGE_SIZE;
+ f64->uc.uc_mcontext.gregs[REG_CSGSFS] = 0x33; /* __USER_CS */
+
+ f32 = retsite + 0x800;
+ f32->ip = (unsigned long)retsite;
+ f32->sp = (unsigned long)retsite + PAGE_SIZE;
+ f32->cs = 0x33; /* __USER_CS (64-bit) */
+ f32->ss = 0x2b; /* __USER_DS */
+
+ if (crafted_sigreturn((unsigned long)f64 + 8, false, false) != 42) {
+ printf("[FAIL]\trt_sigreturn protection (hijack failed without shadow stack)\n");
+ goto out;
+ }
+ if (crafted_sigreturn((unsigned long)f64 + 8, false, true) != 0x100 + SIGSEGV) {
+ printf("[FAIL]\trt_sigreturn protection\n");
+ goto out;
+ }
+ printf("[OK]\trt_sigreturn protection\n");
+
+ if (crafted_sigreturn((unsigned long)f32 + 4, true, false) != 42) {
+ printf("[SKIP]\tia32 rt_sigreturn protection (int $0x80 unavailable)\n");
+ ret = 0;
+ goto out;
+ }
+ if (crafted_sigreturn((unsigned long)f32 + 4, true, true) != 0x100 + SIGSEGV) {
+ printf("[FAIL]\tia32 rt_sigreturn protection\n");
+ goto out;
+ }
+ printf("[OK]\tia32 rt_sigreturn protection\n");
+ ret = 0;
+
+out:
+ munmap(retsite, PAGE_SIZE);
+ return ret;
+}
+
static int parse_uint_from_file(const char *file, const char *fmt)
{
int err, ret;
@@ -1145,6 +1249,12 @@ int main(int argc, char *argv[])
goto out;
}
+ if (test_sigreturn()) {
+ ret = 1;
+ printf("[FAIL]\tsigreturn test\n");
+ goto out;
+ }
+
if (test_uretprobe()) {
ret = 1;
printf("[FAIL]\turetprobe test\n");
--
2.52.0
next prev parent reply other threads:[~2026-10-08 20:22 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-08 20:16 [PATCH 0/3] x86/shstk: ban ia32 sigreturn Richard Patel
2026-10-08 20:16 ` [PATCH 1/3] x86/shstk: ban ia32 sigreturn when shadow stack is enabled Richard Patel
2026-10-08 20:59 ` Edgecombe, Rick P
2026-10-08 21:50 ` Richard Patel
2026-10-08 22:34 ` Edgecombe, Rick P
2026-10-08 22:47 ` Richard Patel
2026-10-08 23:11 ` Edgecombe, Rick P
2026-10-08 23:29 ` Richard Patel
2026-10-09 11:36 ` Richard Patel
2026-10-09 22:14 ` Edgecombe, Rick P
2026-10-08 20:16 ` Richard Patel [this message]
2026-10-08 20:16 ` [PATCH 3/3] selftests/x86: skip shstk tests where perf_event_open() fails Richard Patel
2026-10-08 21:00 ` [PATCH 0/3] x86/shstk: ban ia32 sigreturn Edgecombe, Rick P
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261008201610.1003569-3-ripatel@wii.dev \
--to=ripatel@wii.dev \
--cc=bp@alien8.de \
--cc=dave.hansen@linux.intel.com \
--cc=hpa@zytor.com \
--cc=kees@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=mingo@redhat.com \
--cc=rick.p.edgecombe@intel.com \
--cc=shuah@kernel.org \
--cc=tglx@kernel.org \
--cc=x86@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.