| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088 |
- // 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.
- */
- #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;
- }
- 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;
- }
- 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;
- }
- 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;
- }
- #endif
|