Run the same test suite for arch_prctl, against prctl, to ensure
consisteny and correctness between the interfaces.

Signed-off-by: Bill Roberts <[email protected]>
---
 tools/testing/selftests/x86/Makefile          |    5 +-
 tools/testing/selftests/x86/shadow_stack.c    | 1205 +++++++++++++++++
 tools/testing/selftests/x86/shadow_stack.h    |   14 +
 .../testing/selftests/x86/test_shadow_stack.c | 1167 +---------------
 .../selftests/x86/test_shadow_stack_prctl.c   |   12 +
 5 files changed, 1242 insertions(+), 1161 deletions(-)
 create mode 100644 tools/testing/selftests/x86/shadow_stack.c
 create mode 100644 tools/testing/selftests/x86/shadow_stack.h
 create mode 100644 tools/testing/selftests/x86/test_shadow_stack_prctl.c

diff --git a/tools/testing/selftests/x86/Makefile 
b/tools/testing/selftests/x86/Makefile
index 434065215d12..8748e2f4a915 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -19,7 +19,8 @@ TARGETS_C_32BIT_ONLY := entry_from_vm86 test_syscall_vdso 
unwind_vdso \
                        test_FCMOV test_FCOMI test_FISTTP \
                        vdso_restorer
 TARGETS_C_64BIT_ONLY := fsgsbase sysret_rip syscall_numbering \
-                       corrupt_xstate_header amx lam test_shadow_stack avx apx
+                       corrupt_xstate_header amx lam test_shadow_stack avx apx 
\
+                       test_shadow_stack_prctl
 # Some selftests require 32bit support enabled also on 64bit systems
 TARGETS_C_32BIT_NEEDED := ldt_gdt ptrace_syscall
 
@@ -138,3 +139,5 @@ $(OUTPUT)/avx_64: CFLAGS += -mno-avx -mno-avx512f
 $(OUTPUT)/amx_64: EXTRA_FILES += xstate.c
 $(OUTPUT)/avx_64: EXTRA_FILES += xstate.c
 $(OUTPUT)/apx_64: EXTRA_FILES += xstate.c
+$(OUTPUT)/test_shadow_stack_64: EXTRA_FILES += shadow_stack.c
+$(OUTPUT)/test_shadow_stack_prctl_64: EXTRA_FILES += shadow_stack.c
diff --git a/tools/testing/selftests/x86/shadow_stack.c 
b/tools/testing/selftests/x86/shadow_stack.c
new file mode 100644
index 000000000000..a375c4ff7a6a
--- /dev/null
+++ b/tools/testing/selftests/x86/shadow_stack.c
@@ -0,0 +1,1205 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define _GNU_SOURCE
+
+#include <sys/syscall.h>
+#include <asm/mman.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/wait.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>
+#include <asm/prctl.h>
+#include <sys/prctl.h>
+#include <stdint.h>
+#include <signal.h>
+#include <pthread.h>
+#include <sys/ioctl.h>
+#include <linux/userfaultfd.h>
+#include <setjmp.h>
+#include <sys/ptrace.h>
+#include <sys/signal.h>
+#include <linux/elf.h>
+#include <linux/perf_event.h>
+
+#include "shadow_stack.h"
+
+/*
+ * Define the ABI defines if needed, so people can run the tests
+ * without building the headers.
+ */
+#ifndef __NR_map_shadow_stack
+#define __NR_map_shadow_stack  453
+
+#define SHADOW_STACK_SET_TOKEN (1ULL << 0)
+
+#define ARCH_SHSTK_ENABLE      0x5001
+#define ARCH_SHSTK_DISABLE     0x5002
+#define ARCH_SHSTK_LOCK                0x5003
+#define ARCH_SHSTK_UNLOCK      0x5004
+#define ARCH_SHSTK_STATUS      0x5005
+
+#define ARCH_SHSTK_SHSTK       (1ULL <<  0)
+#define ARCH_SHSTK_WRSS                (1ULL <<  1)
+
+#define NT_X86_SHSTK   0x204
+#endif
+
+#define SS_SIZE 0x200000
+#define PAGE_SIZE 0x1000
+
+void write_shstk(unsigned long *addr, unsigned long val)
+{
+       asm volatile("wrssq %[val], (%[addr])\n"
+                    : "=m" (addr)
+                    : [addr] "r" (addr), [val] "r" (val));
+}
+
+static inline unsigned long __attribute__((always_inline)) get_ssp(void)
+{
+       unsigned long ret = 0;
+
+       asm volatile("xor %0, %0; rdsspq %0" : "=r" (ret));
+       return ret;
+}
+
+/*
+ * For use in inline enablement of shadow stack.
+ *
+ * The program can't return from the point where shadow stack gets enabled
+ * because there will be no address on the shadow stack. So it can't use
+ * syscall() for enablement, since it is a function.
+ *
+ * Based on code from nolibc.h. Keep a copy here because this can't pull in all
+ * of nolibc.h.
+ */
+#define ARCH_PRCTL(arg1, arg2)                                 \
+({                                                             \
+       long _ret;                                              \
+       register long _num  asm("eax") = __NR_arch_prctl;       \
+       register long _arg1 asm("rdi") = (long)(arg1);          \
+       register long _arg2 asm("rsi") = (long)(arg2);          \
+                                                               \
+       asm volatile (                                          \
+               "syscall\n"                                     \
+               : "=a"(_ret)                                    \
+               : "r"(_arg1), "r"(_arg2),                       \
+                 "0"(_num)                                     \
+               : "rcx", "r11", "memory", "cc"                  \
+       );                                                      \
+       _ret;                                                   \
+})
+
+#define PRCTL(option, arg2, arg3, arg4, arg5)             \
+({                                                        \
+       long _ret;                                        \
+       register long _num  asm("rax") = __NR_prctl;      \
+       register long _arg1 asm("rdi") = (long)(option);  \
+       register long _arg2 asm("rsi") = (long)(arg2);    \
+       register long _arg3 asm("rdx") = (long)(arg3);    \
+       register long _arg4 asm("r10") = (long)(arg4);    \
+       register long _arg5 asm("r8")  = (long)(arg5);    \
+                                                         \
+       asm volatile (                                    \
+               "syscall"                                 \
+               : "=a"(_ret)                              \
+               : "0"(_num),                              \
+                 "r"(_arg1), "r"(_arg2), "r"(_arg3),     \
+                 "r"(_arg4), "r"(_arg5)                  \
+               : "rcx", "r11", "memory", "cc"            \
+       );                                                \
+       _ret;                                             \
+})
+
+#define SHADOW_STACK_DISABLE(which_test)                           \
+       ((which_test) == SHADOW_STACK_TEST_ARCH_PRCTL ?            \
+               ARCH_PRCTL(ARCH_SHSTK_DISABLE, ARCH_SHSTK_SHSTK) : \
+               PRCTL(PR_SET_SHADOW_STACK_STATUS, 0, 0, 0, 0))
+
+#define SHADOW_STACK_ENABLE(which_test)                                        
     \
+       ((which_test) == SHADOW_STACK_TEST_ARCH_PRCTL ?                         
    \
+               ARCH_PRCTL(ARCH_SHSTK_ENABLE, ARCH_SHSTK_SHSTK) :               
    \
+               PRCTL(PR_SET_SHADOW_STACK_STATUS, PR_SHADOW_STACK_ENABLE, 0, 0, 
0))
+
+#define SHADOW_STACK_ENABLE_WRITE(which_test)                                  
 \
+       ((which_test) == SHADOW_STACK_TEST_ARCH_PRCTL ?                         
 \
+               ARCH_PRCTL(ARCH_SHSTK_ENABLE, ARCH_SHSTK_WRSS) :                
\
+               PRCTL(PR_SET_SHADOW_STACK_STATUS,                               
\
+                       PR_SHADOW_STACK_ENABLE|PR_SHADOW_STACK_WRITE, 0, 0, 0))
+
+void *create_shstk(void *addr)
+{
+       return (void *)syscall(__NR_map_shadow_stack, addr, SS_SIZE, 
SHADOW_STACK_SET_TOKEN);
+}
+
+void *create_normal_mem(void *addr)
+{
+       return mmap(addr, SS_SIZE, PROT_READ | PROT_WRITE,
+                   MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
+}
+
+void free_shstk(void *shstk)
+{
+       munmap(shstk, SS_SIZE);
+}
+
+int reset_shstk(void *shstk)
+{
+       return madvise(shstk, SS_SIZE, MADV_DONTNEED);
+}
+
+void try_shstk(unsigned long new_ssp)
+{
+       unsigned long ssp;
+
+       printf("[INFO]\tnew_ssp = %lx, *new_ssp = %lx\n",
+              new_ssp, *((unsigned long *)new_ssp));
+
+       ssp = get_ssp();
+       printf("[INFO]\tchanging ssp from %lx to %lx\n", ssp, new_ssp);
+
+       asm volatile("rstorssp (%0)\n":: "r" (new_ssp));
+       asm volatile("saveprevssp");
+       printf("[INFO]\tssp is now %lx\n", get_ssp());
+
+       /* Switch back to original shadow stack */
+       ssp -= 8;
+       asm volatile("rstorssp (%0)\n":: "r" (ssp));
+       asm volatile("saveprevssp");
+}
+
+int test_shstk_pivot(void)
+{
+       void *shstk = create_shstk(0);
+
+       if (shstk == MAP_FAILED) {
+               printf("[FAIL]\tError creating shadow stack: %d\n", errno);
+               return 1;
+       }
+       try_shstk((unsigned long)shstk + SS_SIZE - 8);
+       free_shstk(shstk);
+
+       printf("[OK]\tShadow stack pivot\n");
+       return 0;
+}
+
+int test_shstk_faults(void)
+{
+       unsigned long *shstk = create_shstk(0);
+
+       /* Read shadow stack, test if it's zero to not get read optimized out */
+       if (*shstk != 0)
+               goto err;
+
+       /* Wrss memory that was already read. */
+       write_shstk(shstk, 1);
+       if (*shstk != 1)
+               goto err;
+
+       /* Page out memory, so we can wrss it again. */
+       if (reset_shstk((void *)shstk))
+               goto err;
+
+       write_shstk(shstk, 1);
+       if (*shstk != 1)
+               goto err;
+
+       printf("[OK]\tShadow stack faults\n");
+       return 0;
+
+err:
+       return 1;
+}
+
+unsigned long saved_ssp;
+unsigned long saved_ssp_val;
+volatile bool segv_triggered;
+
+void __attribute__((noinline)) violate_ss(void)
+{
+       saved_ssp = get_ssp();
+       saved_ssp_val = *(unsigned long *)saved_ssp;
+
+       /* Corrupt shadow stack */
+       printf("[INFO]\tCorrupting shadow stack\n");
+       write_shstk((void *)saved_ssp, 0);
+}
+
+void segv_handler(int signum, siginfo_t *si, void *uc)
+{
+       printf("[INFO]\tGenerated shadow stack violation successfully\n");
+
+       segv_triggered = true;
+
+       /* Fix shadow stack */
+       write_shstk((void *)saved_ssp, saved_ssp_val);
+}
+
+int test_shstk_violation(void)
+{
+       struct sigaction sa = {};
+
+       sa.sa_sigaction = segv_handler;
+       sa.sa_flags = SA_SIGINFO;
+       if (sigaction(SIGSEGV, &sa, NULL))
+               return 1;
+
+       segv_triggered = false;
+
+       /* Make sure segv_triggered is set before violate_ss() */
+       asm volatile("" : : : "memory");
+
+       violate_ss();
+
+       signal(SIGSEGV, SIG_DFL);
+
+       printf("[OK]\tShadow stack violation test\n");
+
+       return !segv_triggered;
+}
+
+/* Gup test state */
+#define MAGIC_VAL 0x12345678
+bool is_shstk_access;
+void *shstk_ptr;
+int fd;
+
+void reset_test_shstk(void *addr)
+{
+       if (shstk_ptr)
+               free_shstk(shstk_ptr);
+       shstk_ptr = create_shstk(addr);
+}
+
+void test_access_fix_handler(int signum, siginfo_t *si, void *uc)
+{
+       printf("[INFO]\tViolation from %s\n", is_shstk_access ? "shstk access" 
: "normal write");
+
+       segv_triggered = true;
+
+       /* Fix shadow stack */
+       if (is_shstk_access) {
+               reset_test_shstk(shstk_ptr);
+               return;
+       }
+
+       free_shstk(shstk_ptr);
+       create_normal_mem(shstk_ptr);
+}
+
+bool test_shstk_access(void *ptr)
+{
+       is_shstk_access = true;
+       segv_triggered = false;
+       write_shstk(ptr, MAGIC_VAL);
+
+       asm volatile("" : : : "memory");
+
+       return segv_triggered;
+}
+
+bool test_write_access(void *ptr)
+{
+       is_shstk_access = false;
+       segv_triggered = false;
+       *(unsigned long *)ptr = MAGIC_VAL;
+
+       asm volatile("" : : : "memory");
+
+       return segv_triggered;
+}
+
+bool gup_write(void *ptr)
+{
+       unsigned long val;
+
+       lseek(fd, (unsigned long)ptr, SEEK_SET);
+       if (write(fd, &val, sizeof(val)) < 0)
+               return 1;
+
+       return 0;
+}
+
+bool gup_read(void *ptr)
+{
+       unsigned long val;
+
+       lseek(fd, (unsigned long)ptr, SEEK_SET);
+       if (read(fd, &val, sizeof(val)) < 0)
+               return 1;
+
+       return 0;
+}
+
+int test_gup(void)
+{
+       struct sigaction sa = {};
+       int status;
+       pid_t pid;
+
+       sa.sa_sigaction = test_access_fix_handler;
+       sa.sa_flags = SA_SIGINFO;
+       if (sigaction(SIGSEGV, &sa, NULL))
+               return 1;
+
+       segv_triggered = false;
+
+       fd = open("/proc/self/mem", O_RDWR);
+       if (fd == -1)
+               return 1;
+
+       reset_test_shstk(0);
+       if (gup_read(shstk_ptr))
+               return 1;
+       if (test_shstk_access(shstk_ptr))
+               return 1;
+       printf("[INFO]\tGup read -> shstk access success\n");
+
+       reset_test_shstk(0);
+       if (gup_write(shstk_ptr))
+               return 1;
+       if (test_shstk_access(shstk_ptr))
+               return 1;
+       printf("[INFO]\tGup write -> shstk access success\n");
+
+       reset_test_shstk(0);
+       if (gup_read(shstk_ptr))
+               return 1;
+       if (!test_write_access(shstk_ptr))
+               return 1;
+       printf("[INFO]\tGup read -> write access success\n");
+
+       reset_test_shstk(0);
+       if (gup_write(shstk_ptr))
+               return 1;
+       if (!test_write_access(shstk_ptr))
+               return 1;
+       printf("[INFO]\tGup write -> write access success\n");
+
+       close(fd);
+
+       /* COW/gup test */
+       reset_test_shstk(0);
+       pid = fork();
+       if (!pid) {
+               fd = open("/proc/self/mem", O_RDWR);
+               if (fd == -1)
+                       exit(1);
+
+               if (gup_write(shstk_ptr)) {
+                       close(fd);
+                       exit(1);
+               }
+               close(fd);
+               exit(0);
+       }
+       waitpid(pid, &status, 0);
+       if (WEXITSTATUS(status)) {
+               printf("[FAIL]\tWrite in child failed\n");
+               return 1;
+       }
+       if (*(unsigned long *)shstk_ptr == MAGIC_VAL) {
+               printf("[FAIL]\tWrite in child wrote through to shared 
memory\n");
+               return 1;
+       }
+
+       printf("[INFO]\tCow gup write -> write access success\n");
+
+       free_shstk(shstk_ptr);
+
+       signal(SIGSEGV, SIG_DFL);
+
+       printf("[OK]\tShadow gup test\n");
+
+       return 0;
+}
+
+int test_mprotect(void)
+{
+       struct sigaction sa = {};
+
+       sa.sa_sigaction = test_access_fix_handler;
+       sa.sa_flags = SA_SIGINFO;
+       if (sigaction(SIGSEGV, &sa, NULL))
+               return 1;
+
+       segv_triggered = false;
+
+       /* mprotect a shadow stack as read only */
+       reset_test_shstk(0);
+       if (mprotect(shstk_ptr, SS_SIZE, PROT_READ) < 0) {
+               printf("[FAIL]\tmprotect(PROT_READ) failed\n");
+               return 1;
+       }
+
+       /* try to wrss it and fail */
+       if (!test_shstk_access(shstk_ptr)) {
+               printf("[FAIL]\tShadow stack access to read-only memory 
succeeded\n");
+               return 1;
+       }
+
+       /*
+        * The shadow stack was reset above to resolve the fault, make the new 
one
+        * read-only.
+        */
+       if (mprotect(shstk_ptr, SS_SIZE, PROT_READ) < 0) {
+               printf("[FAIL]\tmprotect(PROT_READ) failed\n");
+               return 1;
+       }
+
+       /* then back to writable */
+       if (mprotect(shstk_ptr, SS_SIZE, PROT_WRITE | PROT_READ) < 0) {
+               printf("[FAIL]\tmprotect(PROT_WRITE) failed\n");
+               return 1;
+       }
+
+       /* then wrss to it and succeed */
+       if (test_shstk_access(shstk_ptr)) {
+               printf("[FAIL]\tShadow stack access to mprotect() writable 
memory failed\n");
+               return 1;
+       }
+
+       free_shstk(shstk_ptr);
+
+       signal(SIGSEGV, SIG_DFL);
+
+       printf("[OK]\tmprotect() test\n");
+
+       return 0;
+}
+
+char zero[4096];
+
+static void *uffd_thread(void *arg)
+{
+       struct uffdio_copy req;
+       int uffd = *(int *)arg;
+       struct uffd_msg msg;
+       int ret;
+
+       while (1) {
+               ret = read(uffd, &msg, sizeof(msg));
+               if (ret > 0)
+                       break;
+               else if (errno == EAGAIN)
+                       continue;
+               return (void *)1;
+       }
+
+       req.dst = msg.arg.pagefault.address;
+       req.src = (__u64)zero;
+       req.len = 4096;
+       req.mode = 0;
+
+       if (ioctl(uffd, UFFDIO_COPY, &req))
+               return (void *)1;
+
+       return (void *)0;
+}
+
+int test_userfaultfd(void)
+{
+       struct uffdio_register uffdio_register;
+       struct uffdio_api uffdio_api;
+       struct sigaction sa = {};
+       pthread_t thread;
+       void *res;
+       int uffd;
+
+       sa.sa_sigaction = test_access_fix_handler;
+       sa.sa_flags = SA_SIGINFO;
+       if (sigaction(SIGSEGV, &sa, NULL))
+               return 1;
+
+       uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
+       if (uffd < 0) {
+               printf("[SKIP]\tUserfaultfd unavailable.\n");
+               return 0;
+       }
+
+       reset_test_shstk(0);
+
+       uffdio_api.api = UFFD_API;
+       uffdio_api.features = 0;
+       if (ioctl(uffd, UFFDIO_API, &uffdio_api))
+               goto err;
+
+       uffdio_register.range.start = (__u64)shstk_ptr;
+       uffdio_register.range.len = 4096;
+       uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
+       if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register))
+               goto err;
+
+       if (pthread_create(&thread, NULL, &uffd_thread, &uffd))
+               goto err;
+
+       reset_shstk(shstk_ptr);
+       test_shstk_access(shstk_ptr);
+
+       if (pthread_join(thread, &res))
+               goto err;
+
+       if (test_shstk_access(shstk_ptr))
+               goto err;
+
+       free_shstk(shstk_ptr);
+
+       signal(SIGSEGV, SIG_DFL);
+
+       if (!res)
+               printf("[OK]\tUserfaultfd test\n");
+       return !!res;
+err:
+       free_shstk(shstk_ptr);
+       close(uffd);
+       signal(SIGSEGV, SIG_DFL);
+       return 1;
+}
+
+/* Simple linked list for keeping track of mappings in test_guard_gap() */
+struct node {
+       struct node *next;
+       void *mapping;
+};
+
+/*
+ * This tests whether mmap will place other mappings in a shadow stack's guard
+ * gap. The steps are:
+ *   1. Finds an empty place by mapping and unmapping something.
+ *   2. Map a shadow stack in the middle of the known empty area.
+ *   3. Map a bunch of PAGE_SIZE mappings. These will use the search down
+ *      direction, filling any gaps until it encounters the shadow stack's
+ *      guard gap.
+ *   4. When a mapping lands below the shadow stack from step 2, then all
+ *      of the above gaps are filled. The search down algorithm will have
+ *      looked at the shadow stack gaps.
+ *   5. See if it landed in the gap.
+ */
+int test_guard_gap_other_gaps(void)
+{
+       void *free_area, *shstk, *test_map = (void *)0xFFFFFFFFFFFFFFFF;
+       struct node *head = NULL, *cur;
+
+       free_area = mmap(0, SS_SIZE * 3, PROT_READ | PROT_WRITE,
+                        MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+       munmap(free_area, SS_SIZE * 3);
+
+       shstk = create_shstk(free_area + SS_SIZE);
+       if (shstk == MAP_FAILED)
+               return 1;
+
+       while (test_map > shstk) {
+               test_map = mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE,
+                               MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+               if (test_map == MAP_FAILED)
+                       return 1;
+               cur = malloc(sizeof(*cur));
+               cur->mapping = test_map;
+
+               cur->next = head;
+               head = cur;
+       }
+
+       while (head) {
+               cur = head;
+               head = cur->next;
+               munmap(cur->mapping, PAGE_SIZE);
+               free(cur);
+       }
+
+       free_shstk(shstk);
+
+       if (shstk - test_map - PAGE_SIZE != PAGE_SIZE)
+               return 1;
+
+       printf("[OK]\tGuard gap test, other mapping's gaps\n");
+
+       return 0;
+}
+
+/* Tests respecting the guard gap of the mapping getting placed */
+int test_guard_gap_new_mappings_gaps(void)
+{
+       void *free_area, *shstk_start, *test_map = (void *)0xFFFFFFFFFFFFFFFF;
+       struct node *head = NULL, *cur;
+       int ret = 0;
+
+       free_area = mmap(0, PAGE_SIZE * 4, PROT_READ | PROT_WRITE,
+                        MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+       munmap(free_area, PAGE_SIZE * 4);
+
+       /* Test letting map_shadow_stack find a free space */
+       shstk_start = mmap(free_area, PAGE_SIZE, PROT_READ | PROT_WRITE,
+                          MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+       if (shstk_start == MAP_FAILED || shstk_start != free_area)
+               return 1;
+
+       while (test_map > shstk_start) {
+               test_map = (void *)syscall(__NR_map_shadow_stack, 0, PAGE_SIZE, 
0);
+               if (test_map == MAP_FAILED) {
+                       printf("[INFO]\tmap_shadow_stack MAP_FAILED\n");
+                       ret = 1;
+                       break;
+               }
+
+               cur = malloc(sizeof(*cur));
+               cur->mapping = test_map;
+
+               cur->next = head;
+               head = cur;
+
+               if (test_map == free_area + PAGE_SIZE) {
+                       printf("[INFO]\tNew mapping has other mapping in guard 
gap!\n");
+                       ret = 1;
+                       break;
+               }
+       }
+
+       while (head) {
+               cur = head;
+               head = cur->next;
+               munmap(cur->mapping, PAGE_SIZE);
+               free(cur);
+       }
+
+       munmap(shstk_start, PAGE_SIZE);
+
+       if (!ret)
+               printf("[OK]\tGuard gap test, placement mapping's gaps\n");
+
+       return ret;
+}
+
+/*
+ * Too complicated to pull it out of the 32 bit header, but also get the
+ * 64 bit one needed above. Just define a copy here.
+ */
+#define __NR_compat_sigaction 67
+
+/*
+ * Call 32 bit signal handler to get 32 bit signals ABI. Make sure
+ * to push the registers that will get clobbered.
+ */
+int sigaction32(int signum, const struct sigaction *restrict act,
+               struct sigaction *restrict oldact)
+{
+       register long syscall_reg asm("eax") = __NR_compat_sigaction;
+       register long signum_reg asm("ebx") = signum;
+       register long act_reg asm("ecx") = (long)act;
+       register long oldact_reg asm("edx") = (long)oldact;
+       int ret = 0;
+
+       asm volatile ("int $0x80;"
+                     : "=a"(ret), "=m"(oldact)
+                     : "r"(syscall_reg), "r"(signum_reg), "r"(act_reg),
+                       "r"(oldact_reg)
+                     : "r8", "r9", "r10", "r11"
+                    );
+
+       return ret;
+}
+
+sigjmp_buf jmp_buffer;
+enum shadow_stack_test _which_test;
+void segv_gp_handler(int signum, siginfo_t *si, void *uc)
+{
+       segv_triggered = true;
+
+       /*
+        * To work with old glibc, this can't rely on siglongjmp working with
+        * shadow stack enabled, so disable shadow stack before siglongjmp().
+        */
+       SHADOW_STACK_DISABLE(_which_test);
+       siglongjmp(jmp_buffer, -1);
+}
+
+/*
+ * Transition to 32 bit mode and check that a #GP triggers a segfault.
+ */
+int test_32bit(void)
+{
+       struct sigaction sa = {};
+       struct sigaction *sa32;
+
+       /* Create sigaction in 32 bit address range */
+       sa32 = mmap(0, 4096, PROT_READ | PROT_WRITE,
+                   MAP_32BIT | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
+       sa32->sa_flags = SA_SIGINFO;
+
+       sa.sa_sigaction = segv_gp_handler;
+       sa.sa_flags = SA_SIGINFO;
+       if (sigaction(SIGSEGV, &sa, NULL))
+               return 1;
+
+
+       segv_triggered = false;
+
+       /* Make sure segv_triggered is set before triggering the #GP */
+       asm volatile("" : : : "memory");
+
+       /*
+        * Set handler to somewhere in 32 bit address space
+        */
+       sa32->sa_handler = (void *)sa32;
+       if (sigaction32(SIGUSR1, sa32, NULL))
+               return 1;
+
+       if (!sigsetjmp(jmp_buffer, 1))
+               raise(SIGUSR1);
+
+       if (segv_triggered)
+               printf("[OK]\t32 bit test\n");
+
+       return !segv_triggered;
+}
+
+static int parse_uint_from_file(const char *file, const char *fmt)
+{
+       int err, ret;
+       FILE *f;
+
+       f = fopen(file, "re");
+       if (!f) {
+               err = -errno;
+               printf("failed to open '%s': %d\n", file, err);
+               return err;
+       }
+       err = fscanf(f, fmt, &ret);
+       if (err != 1) {
+               err = err == EOF ? -EIO : -errno;
+               printf("failed to parse '%s': %d\n", file, err);
+               fclose(f);
+               return err;
+       }
+       fclose(f);
+       return ret;
+}
+
+static int determine_uprobe_perf_type(void)
+{
+       const char *file = "/sys/bus/event_source/devices/uprobe/type";
+
+       return parse_uint_from_file(file, "%d\n");
+}
+
+static int determine_uprobe_retprobe_bit(void)
+{
+       const char *file = 
"/sys/bus/event_source/devices/uprobe/format/retprobe";
+
+       return parse_uint_from_file(file, "config:%d\n");
+}
+
+static ssize_t get_uprobe_offset(const void *addr)
+{
+       size_t start, end, base;
+       char buf[256];
+       bool found = false;
+       FILE *f;
+
+       f = fopen("/proc/self/maps", "r");
+       if (!f)
+               return -errno;
+
+       while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &base) 
== 4) {
+               if (buf[2] == 'x' && (uintptr_t)addr >= start && 
(uintptr_t)addr < end) {
+                       found = true;
+                       break;
+               }
+       }
+
+       fclose(f);
+
+       if (!found)
+               return -ESRCH;
+
+       return (uintptr_t)addr - start + base;
+}
+
+static __attribute__((noinline)) void uretprobe_trigger(void)
+{
+       asm volatile ("");
+}
+
+/*
+ * This test setups return uprobe, which is sensitive to shadow stack
+ * (crashes without extra fix). After executing the uretprobe we fail
+ * the test if we receive SIGSEGV, no crash means we're good.
+ *
+ * Helper functions above borrowed from bpf selftests.
+ */
+static int test_uretprobe(enum shadow_stack_test which_test)
+{
+       const size_t attr_sz = sizeof(struct perf_event_attr);
+       const char *file = "/proc/self/exe";
+       int bit, fd = 0, 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]\tUretprobe test, uprobes are not 
available\n");
+               return 0;
+       }
+
+       offset = get_uprobe_offset(uretprobe_trigger);
+       if (offset < 0)
+               return 1;
+
+       bit = determine_uprobe_retprobe_bit();
+       if (bit < 0)
+               return 1;
+
+       sa.sa_sigaction = segv_gp_handler;
+       sa.sa_flags = SA_SIGINFO;
+       if (sigaction(SIGSEGV, &sa, NULL))
+               return 1;
+
+       /* Setup return uprobe through perf event interface. */
+       memset(&attr, 0, attr_sz);
+       attr.size = attr_sz;
+       attr.type = type;
+       attr.config = 1 << bit;
+       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;
+
+       SHADOW_STACK_ENABLE(which_test);
+
+       /*
+        * This either segfaults and goes through sigsetjmp above
+        * or succeeds and we're good.
+        */
+       uretprobe_trigger();
+
+       printf("[OK]\tUretprobe test\n");
+       err = 0;
+
+out:
+       SHADOW_STACK_DISABLE(which_test);
+       signal(SIGSEGV, SIG_DFL);
+       if (fd)
+               close(fd);
+       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(enum shadow_stack_test which_test)
+{
+       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 (SHADOW_STACK_ENABLE(which_test))
+               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:
+       SHADOW_STACK_DISABLE(which_test);
+       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. */
+       exit(0);
+}
+
+int test_ptrace(void)
+{
+       unsigned long saved_ssp, ssp = 0;
+       struct sigaction sa = {};
+       struct iovec iov;
+       int status;
+       int pid;
+
+       iov.iov_base = &ssp;
+       iov.iov_len = sizeof(ssp);
+
+       pid = fork();
+       if (!pid) {
+               ssp = get_ssp();
+
+               sa.sa_sigaction = segv_handler_ptrace;
+               sa.sa_flags = SA_SIGINFO;
+               if (sigaction(SIGSEGV, &sa, NULL))
+                       return 1;
+
+               ptrace(PTRACE_TRACEME, NULL, NULL, NULL);
+               /*
+                * The parent will tweak the SSP and return from this function
+                * will #CP.
+                */
+               raise(SIGTRAP);
+
+               exit(1);
+       }
+
+       while (waitpid(pid, &status, 0) != -1 && WSTOPSIG(status) != SIGTRAP);
+
+       if (ptrace(PTRACE_GETREGSET, pid, NT_X86_SHSTK, &iov)) {
+               printf("[INFO]\tFailed to PTRACE_GETREGS\n");
+               goto out_kill;
+       }
+
+       if (!ssp) {
+               printf("[INFO]\tPtrace child SSP was 0\n");
+               goto out_kill;
+       }
+
+       saved_ssp = ssp;
+
+       iov.iov_len = 0;
+       if (!ptrace(PTRACE_SETREGSET, pid, NT_X86_SHSTK, &iov)) {
+               printf("[INFO]\tToo small size accepted via PTRACE_SETREGS\n");
+               goto out_kill;
+       }
+
+       iov.iov_len = sizeof(ssp) + 1;
+       if (!ptrace(PTRACE_SETREGSET, pid, NT_X86_SHSTK, &iov)) {
+               printf("[INFO]\tToo large size accepted via PTRACE_SETREGS\n");
+               goto out_kill;
+       }
+
+       ssp += 1;
+       if (!ptrace(PTRACE_SETREGSET, pid, NT_X86_SHSTK, &iov)) {
+               printf("[INFO]\tUnaligned SSP written via PTRACE_SETREGS\n");
+               goto out_kill;
+       }
+
+       ssp = 0xFFFFFFFFFFFF0000;
+       if (!ptrace(PTRACE_SETREGSET, pid, NT_X86_SHSTK, &iov)) {
+               printf("[INFO]\tKernel range SSP written via PTRACE_SETREGS\n");
+               goto out_kill;
+       }
+
+       /*
+        * Tweak the SSP so the child with #CP when it resumes and returns
+        * from raise()
+        */
+       ssp = saved_ssp + 8;
+       iov.iov_len = sizeof(ssp);
+       if (ptrace(PTRACE_SETREGSET, pid, NT_X86_SHSTK, &iov)) {
+               printf("[INFO]\tFailed to PTRACE_SETREGS\n");
+               goto out_kill;
+       }
+
+       if (ptrace(PTRACE_DETACH, pid, NULL, NULL)) {
+               printf("[INFO]\tFailed to PTRACE_DETACH\n");
+               goto out_kill;
+       }
+
+       waitpid(pid, &status, 0);
+       if (WEXITSTATUS(status))
+               return 1;
+
+       printf("[OK]\tPtrace test\n");
+       return 0;
+
+out_kill:
+       kill(pid, SIGKILL);
+       return 1;
+}
+
+#if (__GNUC__ < 8) || (__GNUC__ == 8 && __GNUC_MINOR__ < 5)
+int shadow_stack_run_tests(enum shadow_stack_test unused)
+{
+       printf("[SKIP]\tCompiler does not support CET.\n");
+       return 0;
+}
+#else
+int shadow_stack_run_tests(enum shadow_stack_test which_test)
+{
+       int ret = 0;
+
+       if (SHADOW_STACK_ENABLE(which_test)) {
+               printf("[SKIP]\tCould not enable Shadow stack\n");
+               return 1;
+       }
+
+       if (SHADOW_STACK_DISABLE(which_test)) {
+               ret = 1;
+               printf("[FAIL]\tDisabling shadow stack failed\n");
+       }
+
+       if (SHADOW_STACK_ENABLE(which_test)) {
+               printf("[SKIP]\tCould not re-enable Shadow stack\n");
+               return 1;
+       }
+
+       if (SHADOW_STACK_ENABLE_WRITE(which_test)) {
+               printf("[SKIP]\tCould not enable WRSS\n");
+               ret = 1;
+               goto out;
+       }
+
+       /* Should have succeeded if here, but this is a test, so double check. 
*/
+       if (!get_ssp()) {
+               printf("[FAIL]\tShadow stack disabled\n");
+               return 1;
+       }
+
+       if (test_shstk_pivot()) {
+               ret = 1;
+               printf("[FAIL]\tShadow stack pivot\n");
+               goto out;
+       }
+
+       if (test_shstk_faults()) {
+               ret = 1;
+               printf("[FAIL]\tShadow stack fault test\n");
+               goto out;
+       }
+
+       if (test_shstk_violation()) {
+               ret = 1;
+               printf("[FAIL]\tShadow stack violation test\n");
+               goto out;
+       }
+
+       if (test_gup()) {
+               ret = 1;
+               printf("[FAIL]\tShadow shadow stack gup\n");
+               goto out;
+       }
+
+       if (test_mprotect()) {
+               ret = 1;
+               printf("[FAIL]\tShadow shadow mprotect test\n");
+               goto out;
+       }
+
+       if (test_userfaultfd()) {
+               ret = 1;
+               printf("[FAIL]\tUserfaultfd test\n");
+               goto out;
+       }
+
+       if (test_guard_gap_other_gaps()) {
+               ret = 1;
+               printf("[FAIL]\tGuard gap test, other mappings' gaps\n");
+               goto out;
+       }
+
+       if (test_guard_gap_new_mappings_gaps()) {
+               ret = 1;
+               printf("[FAIL]\tGuard gap test, placement mapping's gaps\n");
+               goto out;
+       }
+
+       if (test_ptrace()) {
+               ret = 1;
+               printf("[FAIL]\tptrace test\n");
+       }
+
+       if (test_32bit()) {
+               ret = 1;
+               printf("[FAIL]\t32 bit test\n");
+               goto out;
+       }
+
+       if (test_uretprobe(which_test)) {
+               ret = 1;
+               printf("[FAIL]\turetprobe test\n");
+               goto out;
+       }
+
+       if (test_uprobe_call(which_test)) {
+               ret = 1;
+               printf("[FAIL]\tuprobe on CALL test\n");
+               goto out;
+       }
+
+       return ret;
+
+out:
+       /*
+        * Disable shadow stack before the function returns, or there will be a
+        * shadow stack violation.
+        */
+       if (SHADOW_STACK_DISABLE(which_test)) {
+               ret = 1;
+               printf("[FAIL]\tDisabling shadow stack failed\n");
+       }
+
+       return ret;
+}
+#endif
diff --git a/tools/testing/selftests/x86/shadow_stack.h 
b/tools/testing/selftests/x86/shadow_stack.h
new file mode 100644
index 000000000000..d36a44df7387
--- /dev/null
+++ b/tools/testing/selftests/x86/shadow_stack.h
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#ifndef TOOLS_TESTING_SELFTESTS_X86_SHADOW_STACK_H_
+#define TOOLS_TESTING_SELFTESTS_X86_SHADOW_STACK_H_
+
+enum shadow_stack_test {
+       SHADOW_STACK_TEST_ARCH_PRCTL,
+       SHADOW_STACK_TEST_PRCTL,
+       SHADOW_STACK_TEST_COUNT,
+};
+
+int shadow_stack_run_tests(enum shadow_stack_test which_test);
+
+#endif /* TOOLS_TESTING_SELFTESTS_X86_SHADOW_STACK_H_ */
diff --git a/tools/testing/selftests/x86/test_shadow_stack.c 
b/tools/testing/selftests/x86/test_shadow_stack.c
index 3d6ca33edba4..d89c04623001 100644
--- a/tools/testing/selftests/x86/test_shadow_stack.c
+++ b/tools/testing/selftests/x86/test_shadow_stack.c
@@ -1,1174 +1,21 @@
 // SPDX-License-Identifier: GPL-2.0
+
 /*
  * This program test's basic kernel shadow stack support. It enables shadow
  * stack manual via the arch_prctl(), instead of relying on glibc. It's
  * Makefile doesn't compile with shadow stack support, so it doesn't rely on
  * any particular glibc. As a result it can't do any operations that require
  * special glibc shadow stack support (longjmp(), swapcontext(), etc). Just
- * stick to the basics and hope the compiler doesn't do anything strange.
+ * stick to the basics and hope the compiler doesn't do anything strange. It
+ * uses the x86 specific interface for ARCH_PCTL, whereas
+ * test_shadow_stack_prctl.c uses the generic PRCTL interface mixed with x86
+ * specific code.
  */
-
 #define _GNU_SOURCE
 
-#include <sys/syscall.h>
-#include <asm/mman.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/wait.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>
-#include <asm/prctl.h>
-#include <sys/prctl.h>
-#include <stdint.h>
-#include <signal.h>
-#include <pthread.h>
-#include <sys/ioctl.h>
-#include <linux/userfaultfd.h>
-#include <setjmp.h>
-#include <sys/ptrace.h>
-#include <sys/signal.h>
-#include <linux/elf.h>
-#include <linux/perf_event.h>
-
-/*
- * Define the ABI defines if needed, so people can run the tests
- * without building the headers.
- */
-#ifndef __NR_map_shadow_stack
-#define __NR_map_shadow_stack  453
-
-#define SHADOW_STACK_SET_TOKEN (1ULL << 0)
-
-#define ARCH_SHSTK_ENABLE      0x5001
-#define ARCH_SHSTK_DISABLE     0x5002
-#define ARCH_SHSTK_LOCK                0x5003
-#define ARCH_SHSTK_UNLOCK      0x5004
-#define ARCH_SHSTK_STATUS      0x5005
-
-#define ARCH_SHSTK_SHSTK       (1ULL <<  0)
-#define ARCH_SHSTK_WRSS                (1ULL <<  1)
-
-#define NT_X86_SHSTK   0x204
-#endif
-
-#define SS_SIZE 0x200000
-#define PAGE_SIZE 0x1000
-
-#if (__GNUC__ < 8) || (__GNUC__ == 8 && __GNUC_MINOR__ < 5)
-int main(int argc, char *argv[])
-{
-       printf("[SKIP]\tCompiler does not support CET.\n");
-       return 0;
-}
-#else
-void write_shstk(unsigned long *addr, unsigned long val)
-{
-       asm volatile("wrssq %[val], (%[addr])\n"
-                    : "=m" (addr)
-                    : [addr] "r" (addr), [val] "r" (val));
-}
-
-static inline unsigned long __attribute__((always_inline)) get_ssp(void)
-{
-       unsigned long ret = 0;
-
-       asm volatile("xor %0, %0; rdsspq %0" : "=r" (ret));
-       return ret;
-}
-
-/*
- * For use in inline enablement of shadow stack.
- *
- * The program can't return from the point where shadow stack gets enabled
- * because there will be no address on the shadow stack. So it can't use
- * syscall() for enablement, since it is a function.
- *
- * Based on code from nolibc.h. Keep a copy here because this can't pull in all
- * of nolibc.h.
- */
-#define ARCH_PRCTL(arg1, arg2)                                 \
-({                                                             \
-       long _ret;                                              \
-       register long _num  asm("eax") = __NR_arch_prctl;       \
-       register long _arg1 asm("rdi") = (long)(arg1);          \
-       register long _arg2 asm("rsi") = (long)(arg2);          \
-                                                               \
-       asm volatile (                                          \
-               "syscall\n"                                     \
-               : "=a"(_ret)                                    \
-               : "r"(_arg1), "r"(_arg2),                       \
-                 "0"(_num)                                     \
-               : "rcx", "r11", "memory", "cc"                  \
-       );                                                      \
-       _ret;                                                   \
-})
-
-void *create_shstk(void *addr)
-{
-       return (void *)syscall(__NR_map_shadow_stack, addr, SS_SIZE, 
SHADOW_STACK_SET_TOKEN);
-}
-
-void *create_normal_mem(void *addr)
-{
-       return mmap(addr, SS_SIZE, PROT_READ | PROT_WRITE,
-                   MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
-}
-
-void free_shstk(void *shstk)
-{
-       munmap(shstk, SS_SIZE);
-}
-
-int reset_shstk(void *shstk)
-{
-       return madvise(shstk, SS_SIZE, MADV_DONTNEED);
-}
-
-void try_shstk(unsigned long new_ssp)
-{
-       unsigned long ssp;
-
-       printf("[INFO]\tnew_ssp = %lx, *new_ssp = %lx\n",
-              new_ssp, *((unsigned long *)new_ssp));
-
-       ssp = get_ssp();
-       printf("[INFO]\tchanging ssp from %lx to %lx\n", ssp, new_ssp);
-
-       asm volatile("rstorssp (%0)\n":: "r" (new_ssp));
-       asm volatile("saveprevssp");
-       printf("[INFO]\tssp is now %lx\n", get_ssp());
-
-       /* Switch back to original shadow stack */
-       ssp -= 8;
-       asm volatile("rstorssp (%0)\n":: "r" (ssp));
-       asm volatile("saveprevssp");
-}
-
-int test_shstk_pivot(void)
-{
-       void *shstk = create_shstk(0);
-
-       if (shstk == MAP_FAILED) {
-               printf("[FAIL]\tError creating shadow stack: %d\n", errno);
-               return 1;
-       }
-       try_shstk((unsigned long)shstk + SS_SIZE - 8);
-       free_shstk(shstk);
-
-       printf("[OK]\tShadow stack pivot\n");
-       return 0;
-}
-
-int test_shstk_faults(void)
-{
-       unsigned long *shstk = create_shstk(0);
-
-       /* Read shadow stack, test if it's zero to not get read optimized out */
-       if (*shstk != 0)
-               goto err;
-
-       /* Wrss memory that was already read. */
-       write_shstk(shstk, 1);
-       if (*shstk != 1)
-               goto err;
-
-       /* Page out memory, so we can wrss it again. */
-       if (reset_shstk((void *)shstk))
-               goto err;
-
-       write_shstk(shstk, 1);
-       if (*shstk != 1)
-               goto err;
-
-       printf("[OK]\tShadow stack faults\n");
-       return 0;
-
-err:
-       return 1;
-}
-
-unsigned long saved_ssp;
-unsigned long saved_ssp_val;
-volatile bool segv_triggered;
-
-void __attribute__((noinline)) violate_ss(void)
-{
-       saved_ssp = get_ssp();
-       saved_ssp_val = *(unsigned long *)saved_ssp;
-
-       /* Corrupt shadow stack */
-       printf("[INFO]\tCorrupting shadow stack\n");
-       write_shstk((void *)saved_ssp, 0);
-}
-
-void segv_handler(int signum, siginfo_t *si, void *uc)
-{
-       printf("[INFO]\tGenerated shadow stack violation successfully\n");
-
-       segv_triggered = true;
-
-       /* Fix shadow stack */
-       write_shstk((void *)saved_ssp, saved_ssp_val);
-}
-
-int test_shstk_violation(void)
-{
-       struct sigaction sa = {};
-
-       sa.sa_sigaction = segv_handler;
-       sa.sa_flags = SA_SIGINFO;
-       if (sigaction(SIGSEGV, &sa, NULL))
-               return 1;
-
-       segv_triggered = false;
-
-       /* Make sure segv_triggered is set before violate_ss() */
-       asm volatile("" : : : "memory");
-
-       violate_ss();
-
-       signal(SIGSEGV, SIG_DFL);
-
-       printf("[OK]\tShadow stack violation test\n");
-
-       return !segv_triggered;
-}
-
-/* Gup test state */
-#define MAGIC_VAL 0x12345678
-bool is_shstk_access;
-void *shstk_ptr;
-int fd;
-
-void reset_test_shstk(void *addr)
-{
-       if (shstk_ptr)
-               free_shstk(shstk_ptr);
-       shstk_ptr = create_shstk(addr);
-}
-
-void test_access_fix_handler(int signum, siginfo_t *si, void *uc)
-{
-       printf("[INFO]\tViolation from %s\n", is_shstk_access ? "shstk access" 
: "normal write");
-
-       segv_triggered = true;
-
-       /* Fix shadow stack */
-       if (is_shstk_access) {
-               reset_test_shstk(shstk_ptr);
-               return;
-       }
-
-       free_shstk(shstk_ptr);
-       create_normal_mem(shstk_ptr);
-}
-
-bool test_shstk_access(void *ptr)
-{
-       is_shstk_access = true;
-       segv_triggered = false;
-       write_shstk(ptr, MAGIC_VAL);
-
-       asm volatile("" : : : "memory");
-
-       return segv_triggered;
-}
-
-bool test_write_access(void *ptr)
-{
-       is_shstk_access = false;
-       segv_triggered = false;
-       *(unsigned long *)ptr = MAGIC_VAL;
-
-       asm volatile("" : : : "memory");
-
-       return segv_triggered;
-}
-
-bool gup_write(void *ptr)
-{
-       unsigned long val;
-
-       lseek(fd, (unsigned long)ptr, SEEK_SET);
-       if (write(fd, &val, sizeof(val)) < 0)
-               return 1;
-
-       return 0;
-}
-
-bool gup_read(void *ptr)
-{
-       unsigned long val;
-
-       lseek(fd, (unsigned long)ptr, SEEK_SET);
-       if (read(fd, &val, sizeof(val)) < 0)
-               return 1;
-
-       return 0;
-}
-
-int test_gup(void)
-{
-       struct sigaction sa = {};
-       int status;
-       pid_t pid;
-
-       sa.sa_sigaction = test_access_fix_handler;
-       sa.sa_flags = SA_SIGINFO;
-       if (sigaction(SIGSEGV, &sa, NULL))
-               return 1;
-
-       segv_triggered = false;
-
-       fd = open("/proc/self/mem", O_RDWR);
-       if (fd == -1)
-               return 1;
-
-       reset_test_shstk(0);
-       if (gup_read(shstk_ptr))
-               return 1;
-       if (test_shstk_access(shstk_ptr))
-               return 1;
-       printf("[INFO]\tGup read -> shstk access success\n");
-
-       reset_test_shstk(0);
-       if (gup_write(shstk_ptr))
-               return 1;
-       if (test_shstk_access(shstk_ptr))
-               return 1;
-       printf("[INFO]\tGup write -> shstk access success\n");
-
-       reset_test_shstk(0);
-       if (gup_read(shstk_ptr))
-               return 1;
-       if (!test_write_access(shstk_ptr))
-               return 1;
-       printf("[INFO]\tGup read -> write access success\n");
-
-       reset_test_shstk(0);
-       if (gup_write(shstk_ptr))
-               return 1;
-       if (!test_write_access(shstk_ptr))
-               return 1;
-       printf("[INFO]\tGup write -> write access success\n");
-
-       close(fd);
-
-       /* COW/gup test */
-       reset_test_shstk(0);
-       pid = fork();
-       if (!pid) {
-               fd = open("/proc/self/mem", O_RDWR);
-               if (fd == -1)
-                       exit(1);
-
-               if (gup_write(shstk_ptr)) {
-                       close(fd);
-                       exit(1);
-               }
-               close(fd);
-               exit(0);
-       }
-       waitpid(pid, &status, 0);
-       if (WEXITSTATUS(status)) {
-               printf("[FAIL]\tWrite in child failed\n");
-               return 1;
-       }
-       if (*(unsigned long *)shstk_ptr == MAGIC_VAL) {
-               printf("[FAIL]\tWrite in child wrote through to shared 
memory\n");
-               return 1;
-       }
-
-       printf("[INFO]\tCow gup write -> write access success\n");
-
-       free_shstk(shstk_ptr);
-
-       signal(SIGSEGV, SIG_DFL);
-
-       printf("[OK]\tShadow gup test\n");
-
-       return 0;
-}
-
-int test_mprotect(void)
-{
-       struct sigaction sa = {};
-
-       sa.sa_sigaction = test_access_fix_handler;
-       sa.sa_flags = SA_SIGINFO;
-       if (sigaction(SIGSEGV, &sa, NULL))
-               return 1;
-
-       segv_triggered = false;
-
-       /* mprotect a shadow stack as read only */
-       reset_test_shstk(0);
-       if (mprotect(shstk_ptr, SS_SIZE, PROT_READ) < 0) {
-               printf("[FAIL]\tmprotect(PROT_READ) failed\n");
-               return 1;
-       }
-
-       /* try to wrss it and fail */
-       if (!test_shstk_access(shstk_ptr)) {
-               printf("[FAIL]\tShadow stack access to read-only memory 
succeeded\n");
-               return 1;
-       }
-
-       /*
-        * The shadow stack was reset above to resolve the fault, make the new 
one
-        * read-only.
-        */
-       if (mprotect(shstk_ptr, SS_SIZE, PROT_READ) < 0) {
-               printf("[FAIL]\tmprotect(PROT_READ) failed\n");
-               return 1;
-       }
-
-       /* then back to writable */
-       if (mprotect(shstk_ptr, SS_SIZE, PROT_WRITE | PROT_READ) < 0) {
-               printf("[FAIL]\tmprotect(PROT_WRITE) failed\n");
-               return 1;
-       }
-
-       /* then wrss to it and succeed */
-       if (test_shstk_access(shstk_ptr)) {
-               printf("[FAIL]\tShadow stack access to mprotect() writable 
memory failed\n");
-               return 1;
-       }
-
-       free_shstk(shstk_ptr);
-
-       signal(SIGSEGV, SIG_DFL);
-
-       printf("[OK]\tmprotect() test\n");
-
-       return 0;
-}
-
-char zero[4096];
-
-static void *uffd_thread(void *arg)
-{
-       struct uffdio_copy req;
-       int uffd = *(int *)arg;
-       struct uffd_msg msg;
-       int ret;
-
-       while (1) {
-               ret = read(uffd, &msg, sizeof(msg));
-               if (ret > 0)
-                       break;
-               else if (errno == EAGAIN)
-                       continue;
-               return (void *)1;
-       }
-
-       req.dst = msg.arg.pagefault.address;
-       req.src = (__u64)zero;
-       req.len = 4096;
-       req.mode = 0;
-
-       if (ioctl(uffd, UFFDIO_COPY, &req))
-               return (void *)1;
-
-       return (void *)0;
-}
-
-int test_userfaultfd(void)
-{
-       struct uffdio_register uffdio_register;
-       struct uffdio_api uffdio_api;
-       struct sigaction sa = {};
-       pthread_t thread;
-       void *res;
-       int uffd;
-
-       sa.sa_sigaction = test_access_fix_handler;
-       sa.sa_flags = SA_SIGINFO;
-       if (sigaction(SIGSEGV, &sa, NULL))
-               return 1;
-
-       uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
-       if (uffd < 0) {
-               printf("[SKIP]\tUserfaultfd unavailable.\n");
-               return 0;
-       }
-
-       reset_test_shstk(0);
-
-       uffdio_api.api = UFFD_API;
-       uffdio_api.features = 0;
-       if (ioctl(uffd, UFFDIO_API, &uffdio_api))
-               goto err;
-
-       uffdio_register.range.start = (__u64)shstk_ptr;
-       uffdio_register.range.len = 4096;
-       uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
-       if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register))
-               goto err;
-
-       if (pthread_create(&thread, NULL, &uffd_thread, &uffd))
-               goto err;
-
-       reset_shstk(shstk_ptr);
-       test_shstk_access(shstk_ptr);
-
-       if (pthread_join(thread, &res))
-               goto err;
-
-       if (test_shstk_access(shstk_ptr))
-               goto err;
-
-       free_shstk(shstk_ptr);
-
-       signal(SIGSEGV, SIG_DFL);
-
-       if (!res)
-               printf("[OK]\tUserfaultfd test\n");
-       return !!res;
-err:
-       free_shstk(shstk_ptr);
-       close(uffd);
-       signal(SIGSEGV, SIG_DFL);
-       return 1;
-}
-
-/* Simple linked list for keeping track of mappings in test_guard_gap() */
-struct node {
-       struct node *next;
-       void *mapping;
-};
-
-/*
- * This tests whether mmap will place other mappings in a shadow stack's guard
- * gap. The steps are:
- *   1. Finds an empty place by mapping and unmapping something.
- *   2. Map a shadow stack in the middle of the known empty area.
- *   3. Map a bunch of PAGE_SIZE mappings. These will use the search down
- *      direction, filling any gaps until it encounters the shadow stack's
- *      guard gap.
- *   4. When a mapping lands below the shadow stack from step 2, then all
- *      of the above gaps are filled. The search down algorithm will have
- *      looked at the shadow stack gaps.
- *   5. See if it landed in the gap.
- */
-int test_guard_gap_other_gaps(void)
-{
-       void *free_area, *shstk, *test_map = (void *)0xFFFFFFFFFFFFFFFF;
-       struct node *head = NULL, *cur;
-
-       free_area = mmap(0, SS_SIZE * 3, PROT_READ | PROT_WRITE,
-                        MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-       munmap(free_area, SS_SIZE * 3);
-
-       shstk = create_shstk(free_area + SS_SIZE);
-       if (shstk == MAP_FAILED)
-               return 1;
-
-       while (test_map > shstk) {
-               test_map = mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE,
-                               MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-               if (test_map == MAP_FAILED)
-                       return 1;
-               cur = malloc(sizeof(*cur));
-               cur->mapping = test_map;
-
-               cur->next = head;
-               head = cur;
-       }
-
-       while (head) {
-               cur = head;
-               head = cur->next;
-               munmap(cur->mapping, PAGE_SIZE);
-               free(cur);
-       }
-
-       free_shstk(shstk);
-
-       if (shstk - test_map - PAGE_SIZE != PAGE_SIZE)
-               return 1;
-
-       printf("[OK]\tGuard gap test, other mapping's gaps\n");
-
-       return 0;
-}
-
-/* Tests respecting the guard gap of the mapping getting placed */
-int test_guard_gap_new_mappings_gaps(void)
-{
-       void *free_area, *shstk_start, *test_map = (void *)0xFFFFFFFFFFFFFFFF;
-       struct node *head = NULL, *cur;
-       int ret = 0;
-
-       free_area = mmap(0, PAGE_SIZE * 4, PROT_READ | PROT_WRITE,
-                        MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-       munmap(free_area, PAGE_SIZE * 4);
-
-       /* Test letting map_shadow_stack find a free space */
-       shstk_start = mmap(free_area, PAGE_SIZE, PROT_READ | PROT_WRITE,
-                          MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-       if (shstk_start == MAP_FAILED || shstk_start != free_area)
-               return 1;
-
-       while (test_map > shstk_start) {
-               test_map = (void *)syscall(__NR_map_shadow_stack, 0, PAGE_SIZE, 
0);
-               if (test_map == MAP_FAILED) {
-                       printf("[INFO]\tmap_shadow_stack MAP_FAILED\n");
-                       ret = 1;
-                       break;
-               }
-
-               cur = malloc(sizeof(*cur));
-               cur->mapping = test_map;
-
-               cur->next = head;
-               head = cur;
-
-               if (test_map == free_area + PAGE_SIZE) {
-                       printf("[INFO]\tNew mapping has other mapping in guard 
gap!\n");
-                       ret = 1;
-                       break;
-               }
-       }
-
-       while (head) {
-               cur = head;
-               head = cur->next;
-               munmap(cur->mapping, PAGE_SIZE);
-               free(cur);
-       }
-
-       munmap(shstk_start, PAGE_SIZE);
-
-       if (!ret)
-               printf("[OK]\tGuard gap test, placement mapping's gaps\n");
-
-       return ret;
-}
-
-/*
- * Too complicated to pull it out of the 32 bit header, but also get the
- * 64 bit one needed above. Just define a copy here.
- */
-#define __NR_compat_sigaction 67
-
-/*
- * Call 32 bit signal handler to get 32 bit signals ABI. Make sure
- * to push the registers that will get clobbered.
- */
-int sigaction32(int signum, const struct sigaction *restrict act,
-               struct sigaction *restrict oldact)
-{
-       register long syscall_reg asm("eax") = __NR_compat_sigaction;
-       register long signum_reg asm("ebx") = signum;
-       register long act_reg asm("ecx") = (long)act;
-       register long oldact_reg asm("edx") = (long)oldact;
-       int ret = 0;
-
-       asm volatile ("int $0x80;"
-                     : "=a"(ret), "=m"(oldact)
-                     : "r"(syscall_reg), "r"(signum_reg), "r"(act_reg),
-                       "r"(oldact_reg)
-                     : "r8", "r9", "r10", "r11"
-                    );
-
-       return ret;
-}
-
-sigjmp_buf jmp_buffer;
-
-void segv_gp_handler(int signum, siginfo_t *si, void *uc)
-{
-       segv_triggered = true;
-
-       /*
-        * To work with old glibc, this can't rely on siglongjmp working with
-        * shadow stack enabled, so disable shadow stack before siglongjmp().
-        */
-       ARCH_PRCTL(ARCH_SHSTK_DISABLE, ARCH_SHSTK_SHSTK);
-       siglongjmp(jmp_buffer, -1);
-}
-
-/*
- * Transition to 32 bit mode and check that a #GP triggers a segfault.
- */
-int test_32bit(void)
-{
-       struct sigaction sa = {};
-       struct sigaction *sa32;
-
-       /* Create sigaction in 32 bit address range */
-       sa32 = mmap(0, 4096, PROT_READ | PROT_WRITE,
-                   MAP_32BIT | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
-       sa32->sa_flags = SA_SIGINFO;
-
-       sa.sa_sigaction = segv_gp_handler;
-       sa.sa_flags = SA_SIGINFO;
-       if (sigaction(SIGSEGV, &sa, NULL))
-               return 1;
-
-
-       segv_triggered = false;
-
-       /* Make sure segv_triggered is set before triggering the #GP */
-       asm volatile("" : : : "memory");
-
-       /*
-        * Set handler to somewhere in 32 bit address space
-        */
-       sa32->sa_handler = (void *)sa32;
-       if (sigaction32(SIGUSR1, sa32, NULL))
-               return 1;
-
-       if (!sigsetjmp(jmp_buffer, 1))
-               raise(SIGUSR1);
-
-       if (segv_triggered)
-               printf("[OK]\t32 bit test\n");
-
-       return !segv_triggered;
-}
-
-static int parse_uint_from_file(const char *file, const char *fmt)
-{
-       int err, ret;
-       FILE *f;
-
-       f = fopen(file, "re");
-       if (!f) {
-               err = -errno;
-               printf("failed to open '%s': %d\n", file, err);
-               return err;
-       }
-       err = fscanf(f, fmt, &ret);
-       if (err != 1) {
-               err = err == EOF ? -EIO : -errno;
-               printf("failed to parse '%s': %d\n", file, err);
-               fclose(f);
-               return err;
-       }
-       fclose(f);
-       return ret;
-}
-
-static int determine_uprobe_perf_type(void)
-{
-       const char *file = "/sys/bus/event_source/devices/uprobe/type";
-
-       return parse_uint_from_file(file, "%d\n");
-}
-
-static int determine_uprobe_retprobe_bit(void)
-{
-       const char *file = 
"/sys/bus/event_source/devices/uprobe/format/retprobe";
-
-       return parse_uint_from_file(file, "config:%d\n");
-}
-
-static ssize_t get_uprobe_offset(const void *addr)
-{
-       size_t start, end, base;
-       char buf[256];
-       bool found = false;
-       FILE *f;
-
-       f = fopen("/proc/self/maps", "r");
-       if (!f)
-               return -errno;
-
-       while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &base) 
== 4) {
-               if (buf[2] == 'x' && (uintptr_t)addr >= start && 
(uintptr_t)addr < end) {
-                       found = true;
-                       break;
-               }
-       }
-
-       fclose(f);
-
-       if (!found)
-               return -ESRCH;
-
-       return (uintptr_t)addr - start + base;
-}
-
-static __attribute__((noinline)) void uretprobe_trigger(void)
-{
-       asm volatile ("");
-}
-
-/*
- * This test setups return uprobe, which is sensitive to shadow stack
- * (crashes without extra fix). After executing the uretprobe we fail
- * the test if we receive SIGSEGV, no crash means we're good.
- *
- * Helper functions above borrowed from bpf selftests.
- */
-static int test_uretprobe(void)
-{
-       const size_t attr_sz = sizeof(struct perf_event_attr);
-       const char *file = "/proc/self/exe";
-       int bit, fd = 0, 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]\tUretprobe test, uprobes are not 
available\n");
-               return 0;
-       }
-
-       offset = get_uprobe_offset(uretprobe_trigger);
-       if (offset < 0)
-               return 1;
-
-       bit = determine_uprobe_retprobe_bit();
-       if (bit < 0)
-               return 1;
-
-       sa.sa_sigaction = segv_gp_handler;
-       sa.sa_flags = SA_SIGINFO;
-       if (sigaction(SIGSEGV, &sa, NULL))
-               return 1;
-
-       /* Setup return uprobe through perf event interface. */
-       memset(&attr, 0, attr_sz);
-       attr.size = attr_sz;
-       attr.type = type;
-       attr.config = 1 << bit;
-       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;
-
-       ARCH_PRCTL(ARCH_SHSTK_ENABLE, ARCH_SHSTK_SHSTK);
-
-       /*
-        * This either segfaults and goes through sigsetjmp above
-        * or succeeds and we're good.
-        */
-       uretprobe_trigger();
-
-       printf("[OK]\tUretprobe test\n");
-       err = 0;
-
-out:
-       ARCH_PRCTL(ARCH_SHSTK_DISABLE, ARCH_SHSTK_SHSTK);
-       signal(SIGSEGV, SIG_DFL);
-       if (fd)
-               close(fd);
-       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. */
-       exit(0);
-}
-
-int test_ptrace(void)
-{
-       unsigned long saved_ssp, ssp = 0;
-       struct sigaction sa= {};
-       struct iovec iov;
-       int status;
-       int pid;
-
-       iov.iov_base = &ssp;
-       iov.iov_len = sizeof(ssp);
-
-       pid = fork();
-       if (!pid) {
-               ssp = get_ssp();
-
-               sa.sa_sigaction = segv_handler_ptrace;
-               sa.sa_flags = SA_SIGINFO;
-               if (sigaction(SIGSEGV, &sa, NULL))
-                       return 1;
-
-               ptrace(PTRACE_TRACEME, NULL, NULL, NULL);
-               /*
-                * The parent will tweak the SSP and return from this function
-                * will #CP.
-                */
-               raise(SIGTRAP);
-
-               exit(1);
-       }
-
-       while (waitpid(pid, &status, 0) != -1 && WSTOPSIG(status) != SIGTRAP);
-
-       if (ptrace(PTRACE_GETREGSET, pid, NT_X86_SHSTK, &iov)) {
-               printf("[INFO]\tFailed to PTRACE_GETREGS\n");
-               goto out_kill;
-       }
-
-       if (!ssp) {
-               printf("[INFO]\tPtrace child SSP was 0\n");
-               goto out_kill;
-       }
-
-       saved_ssp = ssp;
-
-       iov.iov_len = 0;
-       if (!ptrace(PTRACE_SETREGSET, pid, NT_X86_SHSTK, &iov)) {
-               printf("[INFO]\tToo small size accepted via PTRACE_SETREGS\n");
-               goto out_kill;
-       }
-
-       iov.iov_len = sizeof(ssp) + 1;
-       if (!ptrace(PTRACE_SETREGSET, pid, NT_X86_SHSTK, &iov)) {
-               printf("[INFO]\tToo large size accepted via PTRACE_SETREGS\n");
-               goto out_kill;
-       }
-
-       ssp += 1;
-       if (!ptrace(PTRACE_SETREGSET, pid, NT_X86_SHSTK, &iov)) {
-               printf("[INFO]\tUnaligned SSP written via PTRACE_SETREGS\n");
-               goto out_kill;
-       }
-
-       ssp = 0xFFFFFFFFFFFF0000;
-       if (!ptrace(PTRACE_SETREGSET, pid, NT_X86_SHSTK, &iov)) {
-               printf("[INFO]\tKernel range SSP written via PTRACE_SETREGS\n");
-               goto out_kill;
-       }
-
-       /*
-        * Tweak the SSP so the child with #CP when it resumes and returns
-        * from raise()
-        */
-       ssp = saved_ssp + 8;
-       iov.iov_len = sizeof(ssp);
-       if (ptrace(PTRACE_SETREGSET, pid, NT_X86_SHSTK, &iov)) {
-               printf("[INFO]\tFailed to PTRACE_SETREGS\n");
-               goto out_kill;
-       }
-
-       if (ptrace(PTRACE_DETACH, pid, NULL, NULL)) {
-               printf("[INFO]\tFailed to PTRACE_DETACH\n");
-               goto out_kill;
-       }
-
-       waitpid(pid, &status, 0);
-       if (WEXITSTATUS(status))
-               return 1;
-
-       printf("[OK]\tPtrace test\n");
-       return 0;
-
-out_kill:
-       kill(pid, SIGKILL);
-       return 1;
-}
+#include "shadow_stack.h"
 
 int main(int argc, char *argv[])
 {
-       int ret = 0;
-
-       if (ARCH_PRCTL(ARCH_SHSTK_ENABLE, ARCH_SHSTK_SHSTK)) {
-               printf("[SKIP]\tCould not enable Shadow stack\n");
-               return 1;
-       }
-
-       if (ARCH_PRCTL(ARCH_SHSTK_DISABLE, ARCH_SHSTK_SHSTK)) {
-               ret = 1;
-               printf("[FAIL]\tDisabling shadow stack failed\n");
-       }
-
-       if (ARCH_PRCTL(ARCH_SHSTK_ENABLE, ARCH_SHSTK_SHSTK)) {
-               printf("[SKIP]\tCould not re-enable Shadow stack\n");
-               return 1;
-       }
-
-       if (ARCH_PRCTL(ARCH_SHSTK_ENABLE, ARCH_SHSTK_WRSS)) {
-               printf("[SKIP]\tCould not enable WRSS\n");
-               ret = 1;
-               goto out;
-       }
-
-       /* Should have succeeded if here, but this is a test, so double check. 
*/
-       if (!get_ssp()) {
-               printf("[FAIL]\tShadow stack disabled\n");
-               return 1;
-       }
-
-       if (test_shstk_pivot()) {
-               ret = 1;
-               printf("[FAIL]\tShadow stack pivot\n");
-               goto out;
-       }
-
-       if (test_shstk_faults()) {
-               ret = 1;
-               printf("[FAIL]\tShadow stack fault test\n");
-               goto out;
-       }
-
-       if (test_shstk_violation()) {
-               ret = 1;
-               printf("[FAIL]\tShadow stack violation test\n");
-               goto out;
-       }
-
-       if (test_gup()) {
-               ret = 1;
-               printf("[FAIL]\tShadow shadow stack gup\n");
-               goto out;
-       }
-
-       if (test_mprotect()) {
-               ret = 1;
-               printf("[FAIL]\tShadow shadow mprotect test\n");
-               goto out;
-       }
-
-       if (test_userfaultfd()) {
-               ret = 1;
-               printf("[FAIL]\tUserfaultfd test\n");
-               goto out;
-       }
-
-       if (test_guard_gap_other_gaps()) {
-               ret = 1;
-               printf("[FAIL]\tGuard gap test, other mappings' gaps\n");
-               goto out;
-       }
-
-       if (test_guard_gap_new_mappings_gaps()) {
-               ret = 1;
-               printf("[FAIL]\tGuard gap test, placement mapping's gaps\n");
-               goto out;
-       }
-
-       if (test_ptrace()) {
-               ret = 1;
-               printf("[FAIL]\tptrace test\n");
-       }
-
-       if (test_32bit()) {
-               ret = 1;
-               printf("[FAIL]\t32 bit test\n");
-               goto out;
-       }
-
-       if (test_uretprobe()) {
-               ret = 1;
-               printf("[FAIL]\turetprobe test\n");
-               goto out;
-       }
-
-       if (test_uprobe_call()) {
-               ret = 1;
-               printf("[FAIL]\tuprobe on CALL test\n");
-               goto out;
-       }
-
-       return ret;
-
-out:
-       /*
-        * Disable shadow stack before the function returns, or there will be a
-        * shadow stack violation.
-        */
-       if (ARCH_PRCTL(ARCH_SHSTK_DISABLE, ARCH_SHSTK_SHSTK)) {
-               ret = 1;
-               printf("[FAIL]\tDisabling shadow stack failed\n");
-       }
-
-       return ret;
+       return shadow_stack_run_tests(SHADOW_STACK_TEST_ARCH_PRCTL);
 }
-#endif
diff --git a/tools/testing/selftests/x86/test_shadow_stack_prctl.c 
b/tools/testing/selftests/x86/test_shadow_stack_prctl.c
new file mode 100644
index 000000000000..4641a3b1ca03
--- /dev/null
+++ b/tools/testing/selftests/x86/test_shadow_stack_prctl.c
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Same as test_shadow_stack.c but uses the PRCTL interface
+ * instead of ARCH_PRCTL.
+ */
+#include "shadow_stack.h"
+
+int main(int argc, char *argv[])
+{
+       return shadow_stack_run_tests(SHADOW_STACK_TEST_PRCTL);
+}
-- 
2.55.0


Reply via email to