diff mbox series

[33/35] selftests/x86: Add map_shadow_stack syscall test

Message ID 20220130211838.8382-34-rick.p.edgecombe@intel.com (mailing list archive)
State New
Headers show
Series Shadow stacks for userspace | expand

Commit Message

Edgecombe, Rick P Jan. 30, 2022, 9:18 p.m. UTC
Add a simple selftest for exercising the new map_shadow_stack syscall.

Co-developed-by: Yu, Yu-cheng <yu-cheng.yu@intel.com>
Signed-off-by: Yu, Yu-cheng <yu-cheng.yu@intel.com>
Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
---

v1:
 - New patch.

 tools/testing/selftests/x86/Makefile          |  9 ++-
 .../selftests/x86/test_map_shadow_stack.c     | 75 +++++++++++++++++++
 2 files changed, 83 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/selftests/x86/test_map_shadow_stack.c

Comments

Dave Hansen Feb. 3, 2022, 10:42 p.m. UTC | #1
On 1/30/22 13:18, Rick Edgecombe wrote:
> Add a simple selftest for exercising the new map_shadow_stack syscall.

This is a good start for the selftest.  But, it would be really nice to
see a few additional smoke tests in here that are independent of the
library support.

For instance, it would be nice to have tests that:

1. Write to the shadow stack with normal instructions (and recover from
   the inevitable SEGV).  Make sure the siginfo looks like we expect.
2. Corrupt the regular stack, or maybe just use a retpoline
   do induce a shadow stack exception.  Ditto on checking the siginfo
3. Do enough CALLs that will likely trigger a fault and an on-demand
   shadow stack page allocation.

That will test the *basics* and should be pretty simple to write.
Edgecombe, Rick P Feb. 4, 2022, 1:22 a.m. UTC | #2
On Thu, 2022-02-03 at 14:42 -0800, Dave Hansen wrote:
> This is a good start for the selftest.  But, it would be really nice
> to
> see a few additional smoke tests in here that are independent of the
> library support.

Sure. I had actually included this just because the "adding a syscall"
docs said to make sure to include a test for the syscall. There are
some other tests that were being planned as a follow up.

> 
> For instance, it would be nice to have tests that:
> 
> 1. Write to the shadow stack with normal instructions (and recover
> from
>    the inevitable SEGV).  Make sure the siginfo looks like we expect.
> 2. Corrupt the regular stack, or maybe just use a retpoline
>    do induce a shadow stack exception.  Ditto on checking the siginfo
> 3. Do enough CALLs that will likely trigger a fault and an on-demand
>    shadow stack page allocation.
> 
> That will test the *basics* and should be pretty simple to write.

Most of this already exists in the private tests. I'll combine it into
a single selftest. Having wrss now nicely made it a bit easier because
those writes are treated as shadow stack accesses, so we can do these
operations directly without too much calling acrobatics.

Thanks,

Rick
diff mbox series

Patch

diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
index 8a1f62ab3c8e..9114943336f9 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -9,11 +9,13 @@  UNAME_M := $(shell uname -m)
 CAN_BUILD_I386 := $(shell ./check_cc.sh $(CC) trivial_32bit_program.c -m32)
 CAN_BUILD_X86_64 := $(shell ./check_cc.sh $(CC) trivial_64bit_program.c)
 CAN_BUILD_WITH_NOPIE := $(shell ./check_cc.sh $(CC) trivial_program.c -no-pie)
+CAN_BUILD_WITH_SHSTK := $(shell ./check_cc.sh $(CC) trivial_program.c -mshstk -fcf-protection)
 
 TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt test_mremap_vdso \
 			check_initial_reg_state sigreturn iopl ioperm \
 			test_vsyscall mov_ss_trap \
-			syscall_arg_fault fsgsbase_restore sigaltstack
+			syscall_arg_fault fsgsbase_restore sigaltstack \
+			test_map_shadow_stack
 TARGETS_C_32BIT_ONLY := entry_from_vm86 test_syscall_vdso unwind_vdso \
 			test_FCMOV test_FCOMI test_FISTTP \
 			vdso_restorer
@@ -105,3 +107,8 @@  $(OUTPUT)/test_syscall_vdso_32: thunks_32.S
 # state.
 $(OUTPUT)/check_initial_reg_state_32: CFLAGS += -Wl,-ereal_start -static
 $(OUTPUT)/check_initial_reg_state_64: CFLAGS += -Wl,-ereal_start -static
+
+ifeq ($(CAN_BUILD_WITH_SHSTK),1)
+$(OUTPUT)/test_map_shadow_stack_64: CFLAGS += -mshstk -fcf-protection
+$(OUTPUT)/test_map_shadow_stack_32: CFLAGS += -mshstk -fcf-protection
+endif
\ No newline at end of file
diff --git a/tools/testing/selftests/x86/test_map_shadow_stack.c b/tools/testing/selftests/x86/test_map_shadow_stack.c
new file mode 100644
index 000000000000..dfd94ef0176d
--- /dev/null
+++ b/tools/testing/selftests/x86/test_map_shadow_stack.c
@@ -0,0 +1,75 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+#define _GNU_SOURCE
+
+#include <sys/syscall.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <x86intrin.h>
+
+#define SS_SIZE 0x200000
+
+void *create_shstk(void)
+{
+	return (void *)syscall(__NR_map_shadow_stack, SS_SIZE, SHADOW_STACK_SET_TOKEN);
+}
+
+#if (__GNUC__ < 8) || (__GNUC__ == 8 && __GNUC_MINOR__ < 5)
+int main(int argc, char *argv[])
+{
+	printf("SKIP: compiler does not support CET.");
+	return 0;
+}
+#else
+void try_shstk(unsigned long new_ssp)
+{
+	unsigned long ssp0, ssp1;
+
+	printf("pid=%d\n", getpid());
+	printf("new_ssp = %lx, *new_ssp = %lx\n",
+		new_ssp, *((unsigned long *)new_ssp));
+
+	ssp0 = _get_ssp();
+	printf("changing ssp from %lx to %lx\n", ssp0, new_ssp);
+
+	/* Make sure is aligned to 8 bytes */
+	if ((ssp0 & 0xf) != 0)
+		ssp0 &= -8;
+
+	asm volatile("rstorssp (%0)\n":: "r" (new_ssp));
+	asm volatile("saveprevssp");
+	ssp1 = _get_ssp();
+	printf("ssp is now %lx\n", ssp1);
+
+	ssp0 -= 8;
+	asm volatile("rstorssp (%0)\n":: "r" (ssp0));
+	asm volatile("saveprevssp");
+}
+
+int main(int argc, char *argv[])
+{
+	void *shstk;
+
+	if (!_get_ssp()) {
+		printf("SKIP: shadow stack disabled.");
+		return 0;
+	}
+
+	shstk = create_shstk();
+	if (shstk == MAP_FAILED) {
+		printf("FAIL: Error creating shadow stack: %d\n", errno);
+		return 1;
+	}
+	try_shstk((unsigned long)shstk + SS_SIZE - 8);
+
+	printf("PASS.\n");
+	return 0;
+}
+#endif