162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * This program test's basic kernel shadow stack support. It enables shadow 462306a36Sopenharmony_ci * stack manual via the arch_prctl(), instead of relying on glibc. It's 562306a36Sopenharmony_ci * Makefile doesn't compile with shadow stack support, so it doesn't rely on 662306a36Sopenharmony_ci * any particular glibc. As a result it can't do any operations that require 762306a36Sopenharmony_ci * special glibc shadow stack support (longjmp(), swapcontext(), etc). Just 862306a36Sopenharmony_ci * stick to the basics and hope the compiler doesn't do anything strange. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define _GNU_SOURCE 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <sys/syscall.h> 1462306a36Sopenharmony_ci#include <asm/mman.h> 1562306a36Sopenharmony_ci#include <sys/mman.h> 1662306a36Sopenharmony_ci#include <sys/stat.h> 1762306a36Sopenharmony_ci#include <sys/wait.h> 1862306a36Sopenharmony_ci#include <stdio.h> 1962306a36Sopenharmony_ci#include <stdlib.h> 2062306a36Sopenharmony_ci#include <fcntl.h> 2162306a36Sopenharmony_ci#include <unistd.h> 2262306a36Sopenharmony_ci#include <string.h> 2362306a36Sopenharmony_ci#include <errno.h> 2462306a36Sopenharmony_ci#include <stdbool.h> 2562306a36Sopenharmony_ci#include <x86intrin.h> 2662306a36Sopenharmony_ci#include <asm/prctl.h> 2762306a36Sopenharmony_ci#include <sys/prctl.h> 2862306a36Sopenharmony_ci#include <stdint.h> 2962306a36Sopenharmony_ci#include <signal.h> 3062306a36Sopenharmony_ci#include <pthread.h> 3162306a36Sopenharmony_ci#include <sys/ioctl.h> 3262306a36Sopenharmony_ci#include <linux/userfaultfd.h> 3362306a36Sopenharmony_ci#include <setjmp.h> 3462306a36Sopenharmony_ci#include <sys/ptrace.h> 3562306a36Sopenharmony_ci#include <sys/signal.h> 3662306a36Sopenharmony_ci#include <linux/elf.h> 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* 3962306a36Sopenharmony_ci * Define the ABI defines if needed, so people can run the tests 4062306a36Sopenharmony_ci * without building the headers. 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci#ifndef __NR_map_shadow_stack 4362306a36Sopenharmony_ci#define __NR_map_shadow_stack 453 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define SHADOW_STACK_SET_TOKEN (1ULL << 0) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define ARCH_SHSTK_ENABLE 0x5001 4862306a36Sopenharmony_ci#define ARCH_SHSTK_DISABLE 0x5002 4962306a36Sopenharmony_ci#define ARCH_SHSTK_LOCK 0x5003 5062306a36Sopenharmony_ci#define ARCH_SHSTK_UNLOCK 0x5004 5162306a36Sopenharmony_ci#define ARCH_SHSTK_STATUS 0x5005 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define ARCH_SHSTK_SHSTK (1ULL << 0) 5462306a36Sopenharmony_ci#define ARCH_SHSTK_WRSS (1ULL << 1) 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define NT_X86_SHSTK 0x204 5762306a36Sopenharmony_ci#endif 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define SS_SIZE 0x200000 6062306a36Sopenharmony_ci#define PAGE_SIZE 0x1000 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#if (__GNUC__ < 8) || (__GNUC__ == 8 && __GNUC_MINOR__ < 5) 6362306a36Sopenharmony_ciint main(int argc, char *argv[]) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci printf("[SKIP]\tCompiler does not support CET.\n"); 6662306a36Sopenharmony_ci return 0; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci#else 6962306a36Sopenharmony_civoid write_shstk(unsigned long *addr, unsigned long val) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci asm volatile("wrssq %[val], (%[addr])\n" 7262306a36Sopenharmony_ci : "=m" (addr) 7362306a36Sopenharmony_ci : [addr] "r" (addr), [val] "r" (val)); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic inline unsigned long __attribute__((always_inline)) get_ssp(void) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci unsigned long ret = 0; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci asm volatile("xor %0, %0; rdsspq %0" : "=r" (ret)); 8162306a36Sopenharmony_ci return ret; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* 8562306a36Sopenharmony_ci * For use in inline enablement of shadow stack. 8662306a36Sopenharmony_ci * 8762306a36Sopenharmony_ci * The program can't return from the point where shadow stack gets enabled 8862306a36Sopenharmony_ci * because there will be no address on the shadow stack. So it can't use 8962306a36Sopenharmony_ci * syscall() for enablement, since it is a function. 9062306a36Sopenharmony_ci * 9162306a36Sopenharmony_ci * Based on code from nolibc.h. Keep a copy here because this can't pull in all 9262306a36Sopenharmony_ci * of nolibc.h. 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_ci#define ARCH_PRCTL(arg1, arg2) \ 9562306a36Sopenharmony_ci({ \ 9662306a36Sopenharmony_ci long _ret; \ 9762306a36Sopenharmony_ci register long _num asm("eax") = __NR_arch_prctl; \ 9862306a36Sopenharmony_ci register long _arg1 asm("rdi") = (long)(arg1); \ 9962306a36Sopenharmony_ci register long _arg2 asm("rsi") = (long)(arg2); \ 10062306a36Sopenharmony_ci \ 10162306a36Sopenharmony_ci asm volatile ( \ 10262306a36Sopenharmony_ci "syscall\n" \ 10362306a36Sopenharmony_ci : "=a"(_ret) \ 10462306a36Sopenharmony_ci : "r"(_arg1), "r"(_arg2), \ 10562306a36Sopenharmony_ci "0"(_num) \ 10662306a36Sopenharmony_ci : "rcx", "r11", "memory", "cc" \ 10762306a36Sopenharmony_ci ); \ 10862306a36Sopenharmony_ci _ret; \ 10962306a36Sopenharmony_ci}) 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_civoid *create_shstk(void *addr) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci return (void *)syscall(__NR_map_shadow_stack, addr, SS_SIZE, SHADOW_STACK_SET_TOKEN); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_civoid *create_normal_mem(void *addr) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci return mmap(addr, SS_SIZE, PROT_READ | PROT_WRITE, 11962306a36Sopenharmony_ci MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_civoid free_shstk(void *shstk) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci munmap(shstk, SS_SIZE); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ciint reset_shstk(void *shstk) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci return madvise(shstk, SS_SIZE, MADV_DONTNEED); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_civoid try_shstk(unsigned long new_ssp) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci unsigned long ssp; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci printf("[INFO]\tnew_ssp = %lx, *new_ssp = %lx\n", 13762306a36Sopenharmony_ci new_ssp, *((unsigned long *)new_ssp)); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci ssp = get_ssp(); 14062306a36Sopenharmony_ci printf("[INFO]\tchanging ssp from %lx to %lx\n", ssp, new_ssp); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci asm volatile("rstorssp (%0)\n":: "r" (new_ssp)); 14362306a36Sopenharmony_ci asm volatile("saveprevssp"); 14462306a36Sopenharmony_ci printf("[INFO]\tssp is now %lx\n", get_ssp()); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* Switch back to original shadow stack */ 14762306a36Sopenharmony_ci ssp -= 8; 14862306a36Sopenharmony_ci asm volatile("rstorssp (%0)\n":: "r" (ssp)); 14962306a36Sopenharmony_ci asm volatile("saveprevssp"); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ciint test_shstk_pivot(void) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci void *shstk = create_shstk(0); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (shstk == MAP_FAILED) { 15762306a36Sopenharmony_ci printf("[FAIL]\tError creating shadow stack: %d\n", errno); 15862306a36Sopenharmony_ci return 1; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci try_shstk((unsigned long)shstk + SS_SIZE - 8); 16162306a36Sopenharmony_ci free_shstk(shstk); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci printf("[OK]\tShadow stack pivot\n"); 16462306a36Sopenharmony_ci return 0; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ciint test_shstk_faults(void) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci unsigned long *shstk = create_shstk(0); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* Read shadow stack, test if it's zero to not get read optimized out */ 17262306a36Sopenharmony_ci if (*shstk != 0) 17362306a36Sopenharmony_ci goto err; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* Wrss memory that was already read. */ 17662306a36Sopenharmony_ci write_shstk(shstk, 1); 17762306a36Sopenharmony_ci if (*shstk != 1) 17862306a36Sopenharmony_ci goto err; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* Page out memory, so we can wrss it again. */ 18162306a36Sopenharmony_ci if (reset_shstk((void *)shstk)) 18262306a36Sopenharmony_ci goto err; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci write_shstk(shstk, 1); 18562306a36Sopenharmony_ci if (*shstk != 1) 18662306a36Sopenharmony_ci goto err; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci printf("[OK]\tShadow stack faults\n"); 18962306a36Sopenharmony_ci return 0; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cierr: 19262306a36Sopenharmony_ci return 1; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ciunsigned long saved_ssp; 19662306a36Sopenharmony_ciunsigned long saved_ssp_val; 19762306a36Sopenharmony_civolatile bool segv_triggered; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_civoid __attribute__((noinline)) violate_ss(void) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci saved_ssp = get_ssp(); 20262306a36Sopenharmony_ci saved_ssp_val = *(unsigned long *)saved_ssp; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* Corrupt shadow stack */ 20562306a36Sopenharmony_ci printf("[INFO]\tCorrupting shadow stack\n"); 20662306a36Sopenharmony_ci write_shstk((void *)saved_ssp, 0); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_civoid segv_handler(int signum, siginfo_t *si, void *uc) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci printf("[INFO]\tGenerated shadow stack violation successfully\n"); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci segv_triggered = true; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* Fix shadow stack */ 21662306a36Sopenharmony_ci write_shstk((void *)saved_ssp, saved_ssp_val); 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ciint test_shstk_violation(void) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci struct sigaction sa = {}; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci sa.sa_sigaction = segv_handler; 22462306a36Sopenharmony_ci sa.sa_flags = SA_SIGINFO; 22562306a36Sopenharmony_ci if (sigaction(SIGSEGV, &sa, NULL)) 22662306a36Sopenharmony_ci return 1; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci segv_triggered = false; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* Make sure segv_triggered is set before violate_ss() */ 23162306a36Sopenharmony_ci asm volatile("" : : : "memory"); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci violate_ss(); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci signal(SIGSEGV, SIG_DFL); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci printf("[OK]\tShadow stack violation test\n"); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return !segv_triggered; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci/* Gup test state */ 24362306a36Sopenharmony_ci#define MAGIC_VAL 0x12345678 24462306a36Sopenharmony_cibool is_shstk_access; 24562306a36Sopenharmony_civoid *shstk_ptr; 24662306a36Sopenharmony_ciint fd; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_civoid reset_test_shstk(void *addr) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci if (shstk_ptr) 25162306a36Sopenharmony_ci free_shstk(shstk_ptr); 25262306a36Sopenharmony_ci shstk_ptr = create_shstk(addr); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_civoid test_access_fix_handler(int signum, siginfo_t *si, void *uc) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci printf("[INFO]\tViolation from %s\n", is_shstk_access ? "shstk access" : "normal write"); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci segv_triggered = true; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* Fix shadow stack */ 26262306a36Sopenharmony_ci if (is_shstk_access) { 26362306a36Sopenharmony_ci reset_test_shstk(shstk_ptr); 26462306a36Sopenharmony_ci return; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci free_shstk(shstk_ptr); 26862306a36Sopenharmony_ci create_normal_mem(shstk_ptr); 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cibool test_shstk_access(void *ptr) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci is_shstk_access = true; 27462306a36Sopenharmony_ci segv_triggered = false; 27562306a36Sopenharmony_ci write_shstk(ptr, MAGIC_VAL); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci asm volatile("" : : : "memory"); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci return segv_triggered; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cibool test_write_access(void *ptr) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci is_shstk_access = false; 28562306a36Sopenharmony_ci segv_triggered = false; 28662306a36Sopenharmony_ci *(unsigned long *)ptr = MAGIC_VAL; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci asm volatile("" : : : "memory"); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return segv_triggered; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cibool gup_write(void *ptr) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci unsigned long val; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci lseek(fd, (unsigned long)ptr, SEEK_SET); 29862306a36Sopenharmony_ci if (write(fd, &val, sizeof(val)) < 0) 29962306a36Sopenharmony_ci return 1; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci return 0; 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cibool gup_read(void *ptr) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci unsigned long val; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci lseek(fd, (unsigned long)ptr, SEEK_SET); 30962306a36Sopenharmony_ci if (read(fd, &val, sizeof(val)) < 0) 31062306a36Sopenharmony_ci return 1; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci return 0; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ciint test_gup(void) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct sigaction sa = {}; 31862306a36Sopenharmony_ci int status; 31962306a36Sopenharmony_ci pid_t pid; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci sa.sa_sigaction = test_access_fix_handler; 32262306a36Sopenharmony_ci sa.sa_flags = SA_SIGINFO; 32362306a36Sopenharmony_ci if (sigaction(SIGSEGV, &sa, NULL)) 32462306a36Sopenharmony_ci return 1; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci segv_triggered = false; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci fd = open("/proc/self/mem", O_RDWR); 32962306a36Sopenharmony_ci if (fd == -1) 33062306a36Sopenharmony_ci return 1; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci reset_test_shstk(0); 33362306a36Sopenharmony_ci if (gup_read(shstk_ptr)) 33462306a36Sopenharmony_ci return 1; 33562306a36Sopenharmony_ci if (test_shstk_access(shstk_ptr)) 33662306a36Sopenharmony_ci return 1; 33762306a36Sopenharmony_ci printf("[INFO]\tGup read -> shstk access success\n"); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci reset_test_shstk(0); 34062306a36Sopenharmony_ci if (gup_write(shstk_ptr)) 34162306a36Sopenharmony_ci return 1; 34262306a36Sopenharmony_ci if (test_shstk_access(shstk_ptr)) 34362306a36Sopenharmony_ci return 1; 34462306a36Sopenharmony_ci printf("[INFO]\tGup write -> shstk access success\n"); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci reset_test_shstk(0); 34762306a36Sopenharmony_ci if (gup_read(shstk_ptr)) 34862306a36Sopenharmony_ci return 1; 34962306a36Sopenharmony_ci if (!test_write_access(shstk_ptr)) 35062306a36Sopenharmony_ci return 1; 35162306a36Sopenharmony_ci printf("[INFO]\tGup read -> write access success\n"); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci reset_test_shstk(0); 35462306a36Sopenharmony_ci if (gup_write(shstk_ptr)) 35562306a36Sopenharmony_ci return 1; 35662306a36Sopenharmony_ci if (!test_write_access(shstk_ptr)) 35762306a36Sopenharmony_ci return 1; 35862306a36Sopenharmony_ci printf("[INFO]\tGup write -> write access success\n"); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci close(fd); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* COW/gup test */ 36362306a36Sopenharmony_ci reset_test_shstk(0); 36462306a36Sopenharmony_ci pid = fork(); 36562306a36Sopenharmony_ci if (!pid) { 36662306a36Sopenharmony_ci fd = open("/proc/self/mem", O_RDWR); 36762306a36Sopenharmony_ci if (fd == -1) 36862306a36Sopenharmony_ci exit(1); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (gup_write(shstk_ptr)) { 37162306a36Sopenharmony_ci close(fd); 37262306a36Sopenharmony_ci exit(1); 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci close(fd); 37562306a36Sopenharmony_ci exit(0); 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci waitpid(pid, &status, 0); 37862306a36Sopenharmony_ci if (WEXITSTATUS(status)) { 37962306a36Sopenharmony_ci printf("[FAIL]\tWrite in child failed\n"); 38062306a36Sopenharmony_ci return 1; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci if (*(unsigned long *)shstk_ptr == MAGIC_VAL) { 38362306a36Sopenharmony_ci printf("[FAIL]\tWrite in child wrote through to shared memory\n"); 38462306a36Sopenharmony_ci return 1; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci printf("[INFO]\tCow gup write -> write access success\n"); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci free_shstk(shstk_ptr); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci signal(SIGSEGV, SIG_DFL); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci printf("[OK]\tShadow gup test\n"); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci return 0; 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ciint test_mprotect(void) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci struct sigaction sa = {}; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci sa.sa_sigaction = test_access_fix_handler; 40362306a36Sopenharmony_ci sa.sa_flags = SA_SIGINFO; 40462306a36Sopenharmony_ci if (sigaction(SIGSEGV, &sa, NULL)) 40562306a36Sopenharmony_ci return 1; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci segv_triggered = false; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* mprotect a shadow stack as read only */ 41062306a36Sopenharmony_ci reset_test_shstk(0); 41162306a36Sopenharmony_ci if (mprotect(shstk_ptr, SS_SIZE, PROT_READ) < 0) { 41262306a36Sopenharmony_ci printf("[FAIL]\tmprotect(PROT_READ) failed\n"); 41362306a36Sopenharmony_ci return 1; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* try to wrss it and fail */ 41762306a36Sopenharmony_ci if (!test_shstk_access(shstk_ptr)) { 41862306a36Sopenharmony_ci printf("[FAIL]\tShadow stack access to read-only memory succeeded\n"); 41962306a36Sopenharmony_ci return 1; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* 42362306a36Sopenharmony_ci * The shadow stack was reset above to resolve the fault, make the new one 42462306a36Sopenharmony_ci * read-only. 42562306a36Sopenharmony_ci */ 42662306a36Sopenharmony_ci if (mprotect(shstk_ptr, SS_SIZE, PROT_READ) < 0) { 42762306a36Sopenharmony_ci printf("[FAIL]\tmprotect(PROT_READ) failed\n"); 42862306a36Sopenharmony_ci return 1; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* then back to writable */ 43262306a36Sopenharmony_ci if (mprotect(shstk_ptr, SS_SIZE, PROT_WRITE | PROT_READ) < 0) { 43362306a36Sopenharmony_ci printf("[FAIL]\tmprotect(PROT_WRITE) failed\n"); 43462306a36Sopenharmony_ci return 1; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* then wrss to it and succeed */ 43862306a36Sopenharmony_ci if (test_shstk_access(shstk_ptr)) { 43962306a36Sopenharmony_ci printf("[FAIL]\tShadow stack access to mprotect() writable memory failed\n"); 44062306a36Sopenharmony_ci return 1; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci free_shstk(shstk_ptr); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci signal(SIGSEGV, SIG_DFL); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci printf("[OK]\tmprotect() test\n"); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci return 0; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cichar zero[4096]; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic void *uffd_thread(void *arg) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct uffdio_copy req; 45762306a36Sopenharmony_ci int uffd = *(int *)arg; 45862306a36Sopenharmony_ci struct uffd_msg msg; 45962306a36Sopenharmony_ci int ret; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci while (1) { 46262306a36Sopenharmony_ci ret = read(uffd, &msg, sizeof(msg)); 46362306a36Sopenharmony_ci if (ret > 0) 46462306a36Sopenharmony_ci break; 46562306a36Sopenharmony_ci else if (errno == EAGAIN) 46662306a36Sopenharmony_ci continue; 46762306a36Sopenharmony_ci return (void *)1; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci req.dst = msg.arg.pagefault.address; 47162306a36Sopenharmony_ci req.src = (__u64)zero; 47262306a36Sopenharmony_ci req.len = 4096; 47362306a36Sopenharmony_ci req.mode = 0; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (ioctl(uffd, UFFDIO_COPY, &req)) 47662306a36Sopenharmony_ci return (void *)1; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci return (void *)0; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ciint test_userfaultfd(void) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct uffdio_register uffdio_register; 48462306a36Sopenharmony_ci struct uffdio_api uffdio_api; 48562306a36Sopenharmony_ci struct sigaction sa = {}; 48662306a36Sopenharmony_ci pthread_t thread; 48762306a36Sopenharmony_ci void *res; 48862306a36Sopenharmony_ci int uffd; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci sa.sa_sigaction = test_access_fix_handler; 49162306a36Sopenharmony_ci sa.sa_flags = SA_SIGINFO; 49262306a36Sopenharmony_ci if (sigaction(SIGSEGV, &sa, NULL)) 49362306a36Sopenharmony_ci return 1; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); 49662306a36Sopenharmony_ci if (uffd < 0) { 49762306a36Sopenharmony_ci printf("[SKIP]\tUserfaultfd unavailable.\n"); 49862306a36Sopenharmony_ci return 0; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci reset_test_shstk(0); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci uffdio_api.api = UFFD_API; 50462306a36Sopenharmony_ci uffdio_api.features = 0; 50562306a36Sopenharmony_ci if (ioctl(uffd, UFFDIO_API, &uffdio_api)) 50662306a36Sopenharmony_ci goto err; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci uffdio_register.range.start = (__u64)shstk_ptr; 50962306a36Sopenharmony_ci uffdio_register.range.len = 4096; 51062306a36Sopenharmony_ci uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING; 51162306a36Sopenharmony_ci if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register)) 51262306a36Sopenharmony_ci goto err; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (pthread_create(&thread, NULL, &uffd_thread, &uffd)) 51562306a36Sopenharmony_ci goto err; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci reset_shstk(shstk_ptr); 51862306a36Sopenharmony_ci test_shstk_access(shstk_ptr); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (pthread_join(thread, &res)) 52162306a36Sopenharmony_ci goto err; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci if (test_shstk_access(shstk_ptr)) 52462306a36Sopenharmony_ci goto err; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci free_shstk(shstk_ptr); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci signal(SIGSEGV, SIG_DFL); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (!res) 53162306a36Sopenharmony_ci printf("[OK]\tUserfaultfd test\n"); 53262306a36Sopenharmony_ci return !!res; 53362306a36Sopenharmony_cierr: 53462306a36Sopenharmony_ci free_shstk(shstk_ptr); 53562306a36Sopenharmony_ci close(uffd); 53662306a36Sopenharmony_ci signal(SIGSEGV, SIG_DFL); 53762306a36Sopenharmony_ci return 1; 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci/* Simple linked list for keeping track of mappings in test_guard_gap() */ 54162306a36Sopenharmony_cistruct node { 54262306a36Sopenharmony_ci struct node *next; 54362306a36Sopenharmony_ci void *mapping; 54462306a36Sopenharmony_ci}; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci/* 54762306a36Sopenharmony_ci * This tests whether mmap will place other mappings in a shadow stack's guard 54862306a36Sopenharmony_ci * gap. The steps are: 54962306a36Sopenharmony_ci * 1. Finds an empty place by mapping and unmapping something. 55062306a36Sopenharmony_ci * 2. Map a shadow stack in the middle of the known empty area. 55162306a36Sopenharmony_ci * 3. Map a bunch of PAGE_SIZE mappings. These will use the search down 55262306a36Sopenharmony_ci * direction, filling any gaps until it encounters the shadow stack's 55362306a36Sopenharmony_ci * guard gap. 55462306a36Sopenharmony_ci * 4. When a mapping lands below the shadow stack from step 2, then all 55562306a36Sopenharmony_ci * of the above gaps are filled. The search down algorithm will have 55662306a36Sopenharmony_ci * looked at the shadow stack gaps. 55762306a36Sopenharmony_ci * 5. See if it landed in the gap. 55862306a36Sopenharmony_ci */ 55962306a36Sopenharmony_ciint test_guard_gap(void) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci void *free_area, *shstk, *test_map = (void *)0xFFFFFFFFFFFFFFFF; 56262306a36Sopenharmony_ci struct node *head = NULL, *cur; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci free_area = mmap(0, SS_SIZE * 3, PROT_READ | PROT_WRITE, 56562306a36Sopenharmony_ci MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 56662306a36Sopenharmony_ci munmap(free_area, SS_SIZE * 3); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci shstk = create_shstk(free_area + SS_SIZE); 56962306a36Sopenharmony_ci if (shstk == MAP_FAILED) 57062306a36Sopenharmony_ci return 1; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci while (test_map > shstk) { 57362306a36Sopenharmony_ci test_map = mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE, 57462306a36Sopenharmony_ci MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 57562306a36Sopenharmony_ci if (test_map == MAP_FAILED) 57662306a36Sopenharmony_ci return 1; 57762306a36Sopenharmony_ci cur = malloc(sizeof(*cur)); 57862306a36Sopenharmony_ci cur->mapping = test_map; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci cur->next = head; 58162306a36Sopenharmony_ci head = cur; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci while (head) { 58562306a36Sopenharmony_ci cur = head; 58662306a36Sopenharmony_ci head = cur->next; 58762306a36Sopenharmony_ci munmap(cur->mapping, PAGE_SIZE); 58862306a36Sopenharmony_ci free(cur); 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci free_shstk(shstk); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (shstk - test_map - PAGE_SIZE != PAGE_SIZE) 59462306a36Sopenharmony_ci return 1; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci printf("[OK]\tGuard gap test\n"); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci return 0; 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci/* 60262306a36Sopenharmony_ci * Too complicated to pull it out of the 32 bit header, but also get the 60362306a36Sopenharmony_ci * 64 bit one needed above. Just define a copy here. 60462306a36Sopenharmony_ci */ 60562306a36Sopenharmony_ci#define __NR_compat_sigaction 67 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci/* 60862306a36Sopenharmony_ci * Call 32 bit signal handler to get 32 bit signals ABI. Make sure 60962306a36Sopenharmony_ci * to push the registers that will get clobbered. 61062306a36Sopenharmony_ci */ 61162306a36Sopenharmony_ciint sigaction32(int signum, const struct sigaction *restrict act, 61262306a36Sopenharmony_ci struct sigaction *restrict oldact) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci register long syscall_reg asm("eax") = __NR_compat_sigaction; 61562306a36Sopenharmony_ci register long signum_reg asm("ebx") = signum; 61662306a36Sopenharmony_ci register long act_reg asm("ecx") = (long)act; 61762306a36Sopenharmony_ci register long oldact_reg asm("edx") = (long)oldact; 61862306a36Sopenharmony_ci int ret = 0; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci asm volatile ("int $0x80;" 62162306a36Sopenharmony_ci : "=a"(ret), "=m"(oldact) 62262306a36Sopenharmony_ci : "r"(syscall_reg), "r"(signum_reg), "r"(act_reg), 62362306a36Sopenharmony_ci "r"(oldact_reg) 62462306a36Sopenharmony_ci : "r8", "r9", "r10", "r11" 62562306a36Sopenharmony_ci ); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci return ret; 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cisigjmp_buf jmp_buffer; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_civoid segv_gp_handler(int signum, siginfo_t *si, void *uc) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci segv_triggered = true; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci /* 63762306a36Sopenharmony_ci * To work with old glibc, this can't rely on siglongjmp working with 63862306a36Sopenharmony_ci * shadow stack enabled, so disable shadow stack before siglongjmp(). 63962306a36Sopenharmony_ci */ 64062306a36Sopenharmony_ci ARCH_PRCTL(ARCH_SHSTK_DISABLE, ARCH_SHSTK_SHSTK); 64162306a36Sopenharmony_ci siglongjmp(jmp_buffer, -1); 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci/* 64562306a36Sopenharmony_ci * Transition to 32 bit mode and check that a #GP triggers a segfault. 64662306a36Sopenharmony_ci */ 64762306a36Sopenharmony_ciint test_32bit(void) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci struct sigaction sa = {}; 65062306a36Sopenharmony_ci struct sigaction *sa32; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci /* Create sigaction in 32 bit address range */ 65362306a36Sopenharmony_ci sa32 = mmap(0, 4096, PROT_READ | PROT_WRITE, 65462306a36Sopenharmony_ci MAP_32BIT | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); 65562306a36Sopenharmony_ci sa32->sa_flags = SA_SIGINFO; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci sa.sa_sigaction = segv_gp_handler; 65862306a36Sopenharmony_ci sa.sa_flags = SA_SIGINFO; 65962306a36Sopenharmony_ci if (sigaction(SIGSEGV, &sa, NULL)) 66062306a36Sopenharmony_ci return 1; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci segv_triggered = false; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci /* Make sure segv_triggered is set before triggering the #GP */ 66662306a36Sopenharmony_ci asm volatile("" : : : "memory"); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* 66962306a36Sopenharmony_ci * Set handler to somewhere in 32 bit address space 67062306a36Sopenharmony_ci */ 67162306a36Sopenharmony_ci sa32->sa_handler = (void *)sa32; 67262306a36Sopenharmony_ci if (sigaction32(SIGUSR1, sa32, NULL)) 67362306a36Sopenharmony_ci return 1; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci if (!sigsetjmp(jmp_buffer, 1)) 67662306a36Sopenharmony_ci raise(SIGUSR1); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci if (segv_triggered) 67962306a36Sopenharmony_ci printf("[OK]\t32 bit test\n"); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci return !segv_triggered; 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_civoid segv_handler_ptrace(int signum, siginfo_t *si, void *uc) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci /* The SSP adjustment caused a segfault. */ 68762306a36Sopenharmony_ci exit(0); 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ciint test_ptrace(void) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci unsigned long saved_ssp, ssp = 0; 69362306a36Sopenharmony_ci struct sigaction sa= {}; 69462306a36Sopenharmony_ci struct iovec iov; 69562306a36Sopenharmony_ci int status; 69662306a36Sopenharmony_ci int pid; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci iov.iov_base = &ssp; 69962306a36Sopenharmony_ci iov.iov_len = sizeof(ssp); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci pid = fork(); 70262306a36Sopenharmony_ci if (!pid) { 70362306a36Sopenharmony_ci ssp = get_ssp(); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci sa.sa_sigaction = segv_handler_ptrace; 70662306a36Sopenharmony_ci sa.sa_flags = SA_SIGINFO; 70762306a36Sopenharmony_ci if (sigaction(SIGSEGV, &sa, NULL)) 70862306a36Sopenharmony_ci return 1; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci ptrace(PTRACE_TRACEME, NULL, NULL, NULL); 71162306a36Sopenharmony_ci /* 71262306a36Sopenharmony_ci * The parent will tweak the SSP and return from this function 71362306a36Sopenharmony_ci * will #CP. 71462306a36Sopenharmony_ci */ 71562306a36Sopenharmony_ci raise(SIGTRAP); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci exit(1); 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci while (waitpid(pid, &status, 0) != -1 && WSTOPSIG(status) != SIGTRAP); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci if (ptrace(PTRACE_GETREGSET, pid, NT_X86_SHSTK, &iov)) { 72362306a36Sopenharmony_ci printf("[INFO]\tFailed to PTRACE_GETREGS\n"); 72462306a36Sopenharmony_ci goto out_kill; 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci if (!ssp) { 72862306a36Sopenharmony_ci printf("[INFO]\tPtrace child SSP was 0\n"); 72962306a36Sopenharmony_ci goto out_kill; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci saved_ssp = ssp; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci iov.iov_len = 0; 73562306a36Sopenharmony_ci if (!ptrace(PTRACE_SETREGSET, pid, NT_X86_SHSTK, &iov)) { 73662306a36Sopenharmony_ci printf("[INFO]\tToo small size accepted via PTRACE_SETREGS\n"); 73762306a36Sopenharmony_ci goto out_kill; 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci iov.iov_len = sizeof(ssp) + 1; 74162306a36Sopenharmony_ci if (!ptrace(PTRACE_SETREGSET, pid, NT_X86_SHSTK, &iov)) { 74262306a36Sopenharmony_ci printf("[INFO]\tToo large size accepted via PTRACE_SETREGS\n"); 74362306a36Sopenharmony_ci goto out_kill; 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci ssp += 1; 74762306a36Sopenharmony_ci if (!ptrace(PTRACE_SETREGSET, pid, NT_X86_SHSTK, &iov)) { 74862306a36Sopenharmony_ci printf("[INFO]\tUnaligned SSP written via PTRACE_SETREGS\n"); 74962306a36Sopenharmony_ci goto out_kill; 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci ssp = 0xFFFFFFFFFFFF0000; 75362306a36Sopenharmony_ci if (!ptrace(PTRACE_SETREGSET, pid, NT_X86_SHSTK, &iov)) { 75462306a36Sopenharmony_ci printf("[INFO]\tKernel range SSP written via PTRACE_SETREGS\n"); 75562306a36Sopenharmony_ci goto out_kill; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci /* 75962306a36Sopenharmony_ci * Tweak the SSP so the child with #CP when it resumes and returns 76062306a36Sopenharmony_ci * from raise() 76162306a36Sopenharmony_ci */ 76262306a36Sopenharmony_ci ssp = saved_ssp + 8; 76362306a36Sopenharmony_ci iov.iov_len = sizeof(ssp); 76462306a36Sopenharmony_ci if (ptrace(PTRACE_SETREGSET, pid, NT_X86_SHSTK, &iov)) { 76562306a36Sopenharmony_ci printf("[INFO]\tFailed to PTRACE_SETREGS\n"); 76662306a36Sopenharmony_ci goto out_kill; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci if (ptrace(PTRACE_DETACH, pid, NULL, NULL)) { 77062306a36Sopenharmony_ci printf("[INFO]\tFailed to PTRACE_DETACH\n"); 77162306a36Sopenharmony_ci goto out_kill; 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci waitpid(pid, &status, 0); 77562306a36Sopenharmony_ci if (WEXITSTATUS(status)) 77662306a36Sopenharmony_ci return 1; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci printf("[OK]\tPtrace test\n"); 77962306a36Sopenharmony_ci return 0; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ciout_kill: 78262306a36Sopenharmony_ci kill(pid, SIGKILL); 78362306a36Sopenharmony_ci return 1; 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ciint main(int argc, char *argv[]) 78762306a36Sopenharmony_ci{ 78862306a36Sopenharmony_ci int ret = 0; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci if (ARCH_PRCTL(ARCH_SHSTK_ENABLE, ARCH_SHSTK_SHSTK)) { 79162306a36Sopenharmony_ci printf("[SKIP]\tCould not enable Shadow stack\n"); 79262306a36Sopenharmony_ci return 1; 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci if (ARCH_PRCTL(ARCH_SHSTK_DISABLE, ARCH_SHSTK_SHSTK)) { 79662306a36Sopenharmony_ci ret = 1; 79762306a36Sopenharmony_ci printf("[FAIL]\tDisabling shadow stack failed\n"); 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci if (ARCH_PRCTL(ARCH_SHSTK_ENABLE, ARCH_SHSTK_SHSTK)) { 80162306a36Sopenharmony_ci printf("[SKIP]\tCould not re-enable Shadow stack\n"); 80262306a36Sopenharmony_ci return 1; 80362306a36Sopenharmony_ci } 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci if (ARCH_PRCTL(ARCH_SHSTK_ENABLE, ARCH_SHSTK_WRSS)) { 80662306a36Sopenharmony_ci printf("[SKIP]\tCould not enable WRSS\n"); 80762306a36Sopenharmony_ci ret = 1; 80862306a36Sopenharmony_ci goto out; 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci /* Should have succeeded if here, but this is a test, so double check. */ 81262306a36Sopenharmony_ci if (!get_ssp()) { 81362306a36Sopenharmony_ci printf("[FAIL]\tShadow stack disabled\n"); 81462306a36Sopenharmony_ci return 1; 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci if (test_shstk_pivot()) { 81862306a36Sopenharmony_ci ret = 1; 81962306a36Sopenharmony_ci printf("[FAIL]\tShadow stack pivot\n"); 82062306a36Sopenharmony_ci goto out; 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci if (test_shstk_faults()) { 82462306a36Sopenharmony_ci ret = 1; 82562306a36Sopenharmony_ci printf("[FAIL]\tShadow stack fault test\n"); 82662306a36Sopenharmony_ci goto out; 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci if (test_shstk_violation()) { 83062306a36Sopenharmony_ci ret = 1; 83162306a36Sopenharmony_ci printf("[FAIL]\tShadow stack violation test\n"); 83262306a36Sopenharmony_ci goto out; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci if (test_gup()) { 83662306a36Sopenharmony_ci ret = 1; 83762306a36Sopenharmony_ci printf("[FAIL]\tShadow shadow stack gup\n"); 83862306a36Sopenharmony_ci goto out; 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci if (test_mprotect()) { 84262306a36Sopenharmony_ci ret = 1; 84362306a36Sopenharmony_ci printf("[FAIL]\tShadow shadow mprotect test\n"); 84462306a36Sopenharmony_ci goto out; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci if (test_userfaultfd()) { 84862306a36Sopenharmony_ci ret = 1; 84962306a36Sopenharmony_ci printf("[FAIL]\tUserfaultfd test\n"); 85062306a36Sopenharmony_ci goto out; 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci if (test_guard_gap()) { 85462306a36Sopenharmony_ci ret = 1; 85562306a36Sopenharmony_ci printf("[FAIL]\tGuard gap test\n"); 85662306a36Sopenharmony_ci goto out; 85762306a36Sopenharmony_ci } 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci if (test_ptrace()) { 86062306a36Sopenharmony_ci ret = 1; 86162306a36Sopenharmony_ci printf("[FAIL]\tptrace test\n"); 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci if (test_32bit()) { 86562306a36Sopenharmony_ci ret = 1; 86662306a36Sopenharmony_ci printf("[FAIL]\t32 bit test\n"); 86762306a36Sopenharmony_ci goto out; 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci return ret; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ciout: 87362306a36Sopenharmony_ci /* 87462306a36Sopenharmony_ci * Disable shadow stack before the function returns, or there will be a 87562306a36Sopenharmony_ci * shadow stack violation. 87662306a36Sopenharmony_ci */ 87762306a36Sopenharmony_ci if (ARCH_PRCTL(ARCH_SHSTK_DISABLE, ARCH_SHSTK_SHSTK)) { 87862306a36Sopenharmony_ci ret = 1; 87962306a36Sopenharmony_ci printf("[FAIL]\tDisabling shadow stack failed\n"); 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci return ret; 88362306a36Sopenharmony_ci} 88462306a36Sopenharmony_ci#endif 885