1*a505a52bSMark Brown // SPDX-License-Identifier: GPL-2.0-only 2*a505a52bSMark Brown /* 3*a505a52bSMark Brown * Copyright (C) 2023 ARM Limited. 4*a505a52bSMark Brown */ 5*a505a52bSMark Brown 6*a505a52bSMark Brown #define _GNU_SOURCE 7*a505a52bSMark Brown 8*a505a52bSMark Brown #include <pthread.h> 9*a505a52bSMark Brown #include <stdbool.h> 10*a505a52bSMark Brown 11*a505a52bSMark Brown #include <sys/auxv.h> 12*a505a52bSMark Brown #include <sys/mman.h> 13*a505a52bSMark Brown #include <sys/prctl.h> 14*a505a52bSMark Brown #include <sys/ptrace.h> 15*a505a52bSMark Brown #include <sys/uio.h> 16*a505a52bSMark Brown 17*a505a52bSMark Brown #include <asm/hwcap.h> 18*a505a52bSMark Brown #include <asm/mman.h> 19*a505a52bSMark Brown 20*a505a52bSMark Brown #include <linux/compiler.h> 21*a505a52bSMark Brown 22*a505a52bSMark Brown #include "kselftest_harness.h" 23*a505a52bSMark Brown 24*a505a52bSMark Brown #include "gcs-util.h" 25*a505a52bSMark Brown 26*a505a52bSMark Brown #define my_syscall2(num, arg1, arg2) \ 27*a505a52bSMark Brown ({ \ 28*a505a52bSMark Brown register long _num __asm__ ("x8") = (num); \ 29*a505a52bSMark Brown register long _arg1 __asm__ ("x0") = (long)(arg1); \ 30*a505a52bSMark Brown register long _arg2 __asm__ ("x1") = (long)(arg2); \ 31*a505a52bSMark Brown register long _arg3 __asm__ ("x2") = 0; \ 32*a505a52bSMark Brown register long _arg4 __asm__ ("x3") = 0; \ 33*a505a52bSMark Brown register long _arg5 __asm__ ("x4") = 0; \ 34*a505a52bSMark Brown \ 35*a505a52bSMark Brown __asm__ volatile ( \ 36*a505a52bSMark Brown "svc #0\n" \ 37*a505a52bSMark Brown : "=r"(_arg1) \ 38*a505a52bSMark Brown : "r"(_arg1), "r"(_arg2), \ 39*a505a52bSMark Brown "r"(_arg3), "r"(_arg4), \ 40*a505a52bSMark Brown "r"(_arg5), "r"(_num) \ 41*a505a52bSMark Brown : "memory", "cc" \ 42*a505a52bSMark Brown ); \ 43*a505a52bSMark Brown _arg1; \ 44*a505a52bSMark Brown }) 45*a505a52bSMark Brown 46*a505a52bSMark Brown static noinline void gcs_recurse(int depth) 47*a505a52bSMark Brown { 48*a505a52bSMark Brown if (depth) 49*a505a52bSMark Brown gcs_recurse(depth - 1); 50*a505a52bSMark Brown 51*a505a52bSMark Brown /* Prevent tail call optimization so we actually recurse */ 52*a505a52bSMark Brown asm volatile("dsb sy" : : : "memory"); 53*a505a52bSMark Brown } 54*a505a52bSMark Brown 55*a505a52bSMark Brown /* Smoke test that a function call and return works*/ 56*a505a52bSMark Brown TEST(can_call_function) 57*a505a52bSMark Brown { 58*a505a52bSMark Brown gcs_recurse(0); 59*a505a52bSMark Brown } 60*a505a52bSMark Brown 61*a505a52bSMark Brown static void *gcs_test_thread(void *arg) 62*a505a52bSMark Brown { 63*a505a52bSMark Brown int ret; 64*a505a52bSMark Brown unsigned long mode; 65*a505a52bSMark Brown 66*a505a52bSMark Brown /* 67*a505a52bSMark Brown * Some libcs don't seem to fill unused arguments with 0 but 68*a505a52bSMark Brown * the kernel validates this so we supply all 5 arguments. 69*a505a52bSMark Brown */ 70*a505a52bSMark Brown ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0); 71*a505a52bSMark Brown if (ret != 0) { 72*a505a52bSMark Brown ksft_print_msg("PR_GET_SHADOW_STACK_STATUS failed: %d\n", ret); 73*a505a52bSMark Brown return NULL; 74*a505a52bSMark Brown } 75*a505a52bSMark Brown 76*a505a52bSMark Brown if (!(mode & PR_SHADOW_STACK_ENABLE)) { 77*a505a52bSMark Brown ksft_print_msg("GCS not enabled in thread, mode is %lu\n", 78*a505a52bSMark Brown mode); 79*a505a52bSMark Brown return NULL; 80*a505a52bSMark Brown } 81*a505a52bSMark Brown 82*a505a52bSMark Brown /* Just in case... */ 83*a505a52bSMark Brown gcs_recurse(0); 84*a505a52bSMark Brown 85*a505a52bSMark Brown /* Use a non-NULL value to indicate a pass */ 86*a505a52bSMark Brown return &gcs_test_thread; 87*a505a52bSMark Brown } 88*a505a52bSMark Brown 89*a505a52bSMark Brown /* Verify that if we start a new thread it has GCS enabled */ 90*a505a52bSMark Brown TEST(gcs_enabled_thread) 91*a505a52bSMark Brown { 92*a505a52bSMark Brown pthread_t thread; 93*a505a52bSMark Brown void *thread_ret; 94*a505a52bSMark Brown int ret; 95*a505a52bSMark Brown 96*a505a52bSMark Brown ret = pthread_create(&thread, NULL, gcs_test_thread, NULL); 97*a505a52bSMark Brown ASSERT_TRUE(ret == 0); 98*a505a52bSMark Brown if (ret != 0) 99*a505a52bSMark Brown return; 100*a505a52bSMark Brown 101*a505a52bSMark Brown ret = pthread_join(thread, &thread_ret); 102*a505a52bSMark Brown ASSERT_TRUE(ret == 0); 103*a505a52bSMark Brown if (ret != 0) 104*a505a52bSMark Brown return; 105*a505a52bSMark Brown 106*a505a52bSMark Brown ASSERT_TRUE(thread_ret != NULL); 107*a505a52bSMark Brown } 108*a505a52bSMark Brown 109*a505a52bSMark Brown /* Read the GCS until we find the terminator */ 110*a505a52bSMark Brown TEST(gcs_find_terminator) 111*a505a52bSMark Brown { 112*a505a52bSMark Brown unsigned long *gcs, *cur; 113*a505a52bSMark Brown 114*a505a52bSMark Brown gcs = get_gcspr(); 115*a505a52bSMark Brown cur = gcs; 116*a505a52bSMark Brown while (*cur) 117*a505a52bSMark Brown cur++; 118*a505a52bSMark Brown 119*a505a52bSMark Brown ksft_print_msg("GCS in use from %p-%p\n", gcs, cur); 120*a505a52bSMark Brown 121*a505a52bSMark Brown /* 122*a505a52bSMark Brown * We should have at least whatever called into this test so 123*a505a52bSMark Brown * the two pointer should differ. 124*a505a52bSMark Brown */ 125*a505a52bSMark Brown ASSERT_TRUE(gcs != cur); 126*a505a52bSMark Brown } 127*a505a52bSMark Brown 128*a505a52bSMark Brown /* 129*a505a52bSMark Brown * We can access a GCS via ptrace 130*a505a52bSMark Brown * 131*a505a52bSMark Brown * This could usefully have a fixture but note that each test is 132*a505a52bSMark Brown * fork()ed into a new child whcih causes issues. Might be better to 133*a505a52bSMark Brown * lift at least some of this out into a separate, non-harness, test 134*a505a52bSMark Brown * program. 135*a505a52bSMark Brown */ 136*a505a52bSMark Brown TEST(ptrace_read_write) 137*a505a52bSMark Brown { 138*a505a52bSMark Brown pid_t child, pid; 139*a505a52bSMark Brown int ret, status; 140*a505a52bSMark Brown siginfo_t si; 141*a505a52bSMark Brown uint64_t val, rval, gcspr; 142*a505a52bSMark Brown struct user_gcs child_gcs; 143*a505a52bSMark Brown struct iovec iov, local_iov, remote_iov; 144*a505a52bSMark Brown 145*a505a52bSMark Brown child = fork(); 146*a505a52bSMark Brown if (child == -1) { 147*a505a52bSMark Brown ksft_print_msg("fork() failed: %d (%s)\n", 148*a505a52bSMark Brown errno, strerror(errno)); 149*a505a52bSMark Brown ASSERT_NE(child, -1); 150*a505a52bSMark Brown } 151*a505a52bSMark Brown 152*a505a52bSMark Brown if (child == 0) { 153*a505a52bSMark Brown /* 154*a505a52bSMark Brown * In child, make sure there's something on the stack and 155*a505a52bSMark Brown * ask to be traced. 156*a505a52bSMark Brown */ 157*a505a52bSMark Brown gcs_recurse(0); 158*a505a52bSMark Brown if (ptrace(PTRACE_TRACEME, -1, NULL, NULL)) 159*a505a52bSMark Brown ksft_exit_fail_msg("PTRACE_TRACEME %s", 160*a505a52bSMark Brown strerror(errno)); 161*a505a52bSMark Brown 162*a505a52bSMark Brown if (raise(SIGSTOP)) 163*a505a52bSMark Brown ksft_exit_fail_msg("raise(SIGSTOP) %s", 164*a505a52bSMark Brown strerror(errno)); 165*a505a52bSMark Brown 166*a505a52bSMark Brown return; 167*a505a52bSMark Brown } 168*a505a52bSMark Brown 169*a505a52bSMark Brown ksft_print_msg("Child: %d\n", child); 170*a505a52bSMark Brown 171*a505a52bSMark Brown /* Attach to the child */ 172*a505a52bSMark Brown while (1) { 173*a505a52bSMark Brown int sig; 174*a505a52bSMark Brown 175*a505a52bSMark Brown pid = wait(&status); 176*a505a52bSMark Brown if (pid == -1) { 177*a505a52bSMark Brown ksft_print_msg("wait() failed: %s", 178*a505a52bSMark Brown strerror(errno)); 179*a505a52bSMark Brown goto error; 180*a505a52bSMark Brown } 181*a505a52bSMark Brown 182*a505a52bSMark Brown /* 183*a505a52bSMark Brown * This should never happen but it's hard to flag in 184*a505a52bSMark Brown * the framework. 185*a505a52bSMark Brown */ 186*a505a52bSMark Brown if (pid != child) 187*a505a52bSMark Brown continue; 188*a505a52bSMark Brown 189*a505a52bSMark Brown if (WIFEXITED(status) || WIFSIGNALED(status)) 190*a505a52bSMark Brown ksft_exit_fail_msg("Child died unexpectedly\n"); 191*a505a52bSMark Brown 192*a505a52bSMark Brown if (!WIFSTOPPED(status)) 193*a505a52bSMark Brown goto error; 194*a505a52bSMark Brown 195*a505a52bSMark Brown sig = WSTOPSIG(status); 196*a505a52bSMark Brown 197*a505a52bSMark Brown if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &si)) { 198*a505a52bSMark Brown if (errno == ESRCH) { 199*a505a52bSMark Brown ASSERT_NE(errno, ESRCH); 200*a505a52bSMark Brown return; 201*a505a52bSMark Brown } 202*a505a52bSMark Brown 203*a505a52bSMark Brown if (errno == EINVAL) { 204*a505a52bSMark Brown sig = 0; /* bust group-stop */ 205*a505a52bSMark Brown goto cont; 206*a505a52bSMark Brown } 207*a505a52bSMark Brown 208*a505a52bSMark Brown ksft_print_msg("PTRACE_GETSIGINFO: %s\n", 209*a505a52bSMark Brown strerror(errno)); 210*a505a52bSMark Brown goto error; 211*a505a52bSMark Brown } 212*a505a52bSMark Brown 213*a505a52bSMark Brown if (sig == SIGSTOP && si.si_code == SI_TKILL && 214*a505a52bSMark Brown si.si_pid == pid) 215*a505a52bSMark Brown break; 216*a505a52bSMark Brown 217*a505a52bSMark Brown cont: 218*a505a52bSMark Brown if (ptrace(PTRACE_CONT, pid, NULL, sig)) { 219*a505a52bSMark Brown if (errno == ESRCH) { 220*a505a52bSMark Brown ASSERT_NE(errno, ESRCH); 221*a505a52bSMark Brown return; 222*a505a52bSMark Brown } 223*a505a52bSMark Brown 224*a505a52bSMark Brown ksft_print_msg("PTRACE_CONT: %s\n", strerror(errno)); 225*a505a52bSMark Brown goto error; 226*a505a52bSMark Brown } 227*a505a52bSMark Brown } 228*a505a52bSMark Brown 229*a505a52bSMark Brown /* Where is the child GCS? */ 230*a505a52bSMark Brown iov.iov_base = &child_gcs; 231*a505a52bSMark Brown iov.iov_len = sizeof(child_gcs); 232*a505a52bSMark Brown ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_GCS, &iov); 233*a505a52bSMark Brown if (ret != 0) { 234*a505a52bSMark Brown ksft_print_msg("Failed to read child GCS state: %s (%d)\n", 235*a505a52bSMark Brown strerror(errno), errno); 236*a505a52bSMark Brown goto error; 237*a505a52bSMark Brown } 238*a505a52bSMark Brown 239*a505a52bSMark Brown /* We should have inherited GCS over fork(), confirm */ 240*a505a52bSMark Brown if (!(child_gcs.features_enabled & PR_SHADOW_STACK_ENABLE)) { 241*a505a52bSMark Brown ASSERT_TRUE(child_gcs.features_enabled & 242*a505a52bSMark Brown PR_SHADOW_STACK_ENABLE); 243*a505a52bSMark Brown goto error; 244*a505a52bSMark Brown } 245*a505a52bSMark Brown 246*a505a52bSMark Brown gcspr = child_gcs.gcspr_el0; 247*a505a52bSMark Brown ksft_print_msg("Child GCSPR 0x%lx, flags %llx, locked %llx\n", 248*a505a52bSMark Brown gcspr, child_gcs.features_enabled, 249*a505a52bSMark Brown child_gcs.features_locked); 250*a505a52bSMark Brown 251*a505a52bSMark Brown /* Ideally we'd cross check with the child memory map */ 252*a505a52bSMark Brown 253*a505a52bSMark Brown errno = 0; 254*a505a52bSMark Brown val = ptrace(PTRACE_PEEKDATA, child, (void *)gcspr, NULL); 255*a505a52bSMark Brown ret = errno; 256*a505a52bSMark Brown if (ret != 0) 257*a505a52bSMark Brown ksft_print_msg("PTRACE_PEEKDATA failed: %s (%d)\n", 258*a505a52bSMark Brown strerror(ret), ret); 259*a505a52bSMark Brown EXPECT_EQ(ret, 0); 260*a505a52bSMark Brown 261*a505a52bSMark Brown /* The child should be in a function, the GCSPR shouldn't be 0 */ 262*a505a52bSMark Brown EXPECT_NE(val, 0); 263*a505a52bSMark Brown 264*a505a52bSMark Brown /* Same thing via process_vm_readv() */ 265*a505a52bSMark Brown local_iov.iov_base = &rval; 266*a505a52bSMark Brown local_iov.iov_len = sizeof(rval); 267*a505a52bSMark Brown remote_iov.iov_base = (void *)gcspr; 268*a505a52bSMark Brown remote_iov.iov_len = sizeof(rval); 269*a505a52bSMark Brown ret = process_vm_readv(child, &local_iov, 1, &remote_iov, 1, 0); 270*a505a52bSMark Brown if (ret == -1) 271*a505a52bSMark Brown ksft_print_msg("process_vm_readv() failed: %s (%d)\n", 272*a505a52bSMark Brown strerror(errno), errno); 273*a505a52bSMark Brown EXPECT_EQ(ret, sizeof(rval)); 274*a505a52bSMark Brown EXPECT_EQ(val, rval); 275*a505a52bSMark Brown 276*a505a52bSMark Brown /* Write data via a peek */ 277*a505a52bSMark Brown ret = ptrace(PTRACE_POKEDATA, child, (void *)gcspr, NULL); 278*a505a52bSMark Brown if (ret == -1) 279*a505a52bSMark Brown ksft_print_msg("PTRACE_POKEDATA failed: %s (%d)\n", 280*a505a52bSMark Brown strerror(errno), errno); 281*a505a52bSMark Brown EXPECT_EQ(ret, 0); 282*a505a52bSMark Brown EXPECT_EQ(0, ptrace(PTRACE_PEEKDATA, child, (void *)gcspr, NULL)); 283*a505a52bSMark Brown 284*a505a52bSMark Brown /* Restore what we had before */ 285*a505a52bSMark Brown ret = ptrace(PTRACE_POKEDATA, child, (void *)gcspr, val); 286*a505a52bSMark Brown if (ret == -1) 287*a505a52bSMark Brown ksft_print_msg("PTRACE_POKEDATA failed: %s (%d)\n", 288*a505a52bSMark Brown strerror(errno), errno); 289*a505a52bSMark Brown EXPECT_EQ(ret, 0); 290*a505a52bSMark Brown EXPECT_EQ(val, ptrace(PTRACE_PEEKDATA, child, (void *)gcspr, NULL)); 291*a505a52bSMark Brown 292*a505a52bSMark Brown /* That's all, folks */ 293*a505a52bSMark Brown kill(child, SIGKILL); 294*a505a52bSMark Brown return; 295*a505a52bSMark Brown 296*a505a52bSMark Brown error: 297*a505a52bSMark Brown kill(child, SIGKILL); 298*a505a52bSMark Brown ASSERT_FALSE(true); 299*a505a52bSMark Brown } 300*a505a52bSMark Brown 301*a505a52bSMark Brown FIXTURE(map_gcs) 302*a505a52bSMark Brown { 303*a505a52bSMark Brown unsigned long *stack; 304*a505a52bSMark Brown }; 305*a505a52bSMark Brown 306*a505a52bSMark Brown FIXTURE_VARIANT(map_gcs) 307*a505a52bSMark Brown { 308*a505a52bSMark Brown size_t stack_size; 309*a505a52bSMark Brown unsigned long flags; 310*a505a52bSMark Brown }; 311*a505a52bSMark Brown 312*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_gcs, s2k_cap_marker) 313*a505a52bSMark Brown { 314*a505a52bSMark Brown .stack_size = 2 * 1024, 315*a505a52bSMark Brown .flags = SHADOW_STACK_SET_MARKER | SHADOW_STACK_SET_TOKEN, 316*a505a52bSMark Brown }; 317*a505a52bSMark Brown 318*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_gcs, s2k_cap) 319*a505a52bSMark Brown { 320*a505a52bSMark Brown .stack_size = 2 * 1024, 321*a505a52bSMark Brown .flags = SHADOW_STACK_SET_TOKEN, 322*a505a52bSMark Brown }; 323*a505a52bSMark Brown 324*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_gcs, s2k_marker) 325*a505a52bSMark Brown { 326*a505a52bSMark Brown .stack_size = 2 * 1024, 327*a505a52bSMark Brown .flags = SHADOW_STACK_SET_MARKER, 328*a505a52bSMark Brown }; 329*a505a52bSMark Brown 330*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_gcs, s2k) 331*a505a52bSMark Brown { 332*a505a52bSMark Brown .stack_size = 2 * 1024, 333*a505a52bSMark Brown .flags = 0, 334*a505a52bSMark Brown }; 335*a505a52bSMark Brown 336*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_gcs, s4k_cap_marker) 337*a505a52bSMark Brown { 338*a505a52bSMark Brown .stack_size = 4 * 1024, 339*a505a52bSMark Brown .flags = SHADOW_STACK_SET_MARKER | SHADOW_STACK_SET_TOKEN, 340*a505a52bSMark Brown }; 341*a505a52bSMark Brown 342*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_gcs, s4k_cap) 343*a505a52bSMark Brown { 344*a505a52bSMark Brown .stack_size = 4 * 1024, 345*a505a52bSMark Brown .flags = SHADOW_STACK_SET_TOKEN, 346*a505a52bSMark Brown }; 347*a505a52bSMark Brown 348*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_gcs, s3k_marker) 349*a505a52bSMark Brown { 350*a505a52bSMark Brown .stack_size = 4 * 1024, 351*a505a52bSMark Brown .flags = SHADOW_STACK_SET_MARKER, 352*a505a52bSMark Brown }; 353*a505a52bSMark Brown 354*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_gcs, s4k) 355*a505a52bSMark Brown { 356*a505a52bSMark Brown .stack_size = 4 * 1024, 357*a505a52bSMark Brown .flags = 0, 358*a505a52bSMark Brown }; 359*a505a52bSMark Brown 360*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_gcs, s16k_cap_marker) 361*a505a52bSMark Brown { 362*a505a52bSMark Brown .stack_size = 16 * 1024, 363*a505a52bSMark Brown .flags = SHADOW_STACK_SET_MARKER | SHADOW_STACK_SET_TOKEN, 364*a505a52bSMark Brown }; 365*a505a52bSMark Brown 366*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_gcs, s16k_cap) 367*a505a52bSMark Brown { 368*a505a52bSMark Brown .stack_size = 16 * 1024, 369*a505a52bSMark Brown .flags = SHADOW_STACK_SET_TOKEN, 370*a505a52bSMark Brown }; 371*a505a52bSMark Brown 372*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_gcs, s16k_marker) 373*a505a52bSMark Brown { 374*a505a52bSMark Brown .stack_size = 16 * 1024, 375*a505a52bSMark Brown .flags = SHADOW_STACK_SET_MARKER, 376*a505a52bSMark Brown }; 377*a505a52bSMark Brown 378*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_gcs, s16k) 379*a505a52bSMark Brown { 380*a505a52bSMark Brown .stack_size = 16 * 1024, 381*a505a52bSMark Brown .flags = 0, 382*a505a52bSMark Brown }; 383*a505a52bSMark Brown 384*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_gcs, s64k_cap_marker) 385*a505a52bSMark Brown { 386*a505a52bSMark Brown .stack_size = 64 * 1024, 387*a505a52bSMark Brown .flags = SHADOW_STACK_SET_MARKER | SHADOW_STACK_SET_TOKEN, 388*a505a52bSMark Brown }; 389*a505a52bSMark Brown 390*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_gcs, s64k_cap) 391*a505a52bSMark Brown { 392*a505a52bSMark Brown .stack_size = 64 * 1024, 393*a505a52bSMark Brown .flags = SHADOW_STACK_SET_TOKEN, 394*a505a52bSMark Brown }; 395*a505a52bSMark Brown 396*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_gcs, s64k_marker) 397*a505a52bSMark Brown { 398*a505a52bSMark Brown .stack_size = 64 * 1024, 399*a505a52bSMark Brown .flags = SHADOW_STACK_SET_MARKER, 400*a505a52bSMark Brown }; 401*a505a52bSMark Brown 402*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_gcs, s64k) 403*a505a52bSMark Brown { 404*a505a52bSMark Brown .stack_size = 64 * 1024, 405*a505a52bSMark Brown .flags = 0, 406*a505a52bSMark Brown }; 407*a505a52bSMark Brown 408*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_gcs, s128k_cap_marker) 409*a505a52bSMark Brown { 410*a505a52bSMark Brown .stack_size = 128 * 1024, 411*a505a52bSMark Brown .flags = SHADOW_STACK_SET_MARKER | SHADOW_STACK_SET_TOKEN, 412*a505a52bSMark Brown }; 413*a505a52bSMark Brown 414*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_gcs, s128k_cap) 415*a505a52bSMark Brown { 416*a505a52bSMark Brown .stack_size = 128 * 1024, 417*a505a52bSMark Brown .flags = SHADOW_STACK_SET_TOKEN, 418*a505a52bSMark Brown }; 419*a505a52bSMark Brown 420*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_gcs, s128k_marker) 421*a505a52bSMark Brown { 422*a505a52bSMark Brown .stack_size = 128 * 1024, 423*a505a52bSMark Brown .flags = SHADOW_STACK_SET_MARKER, 424*a505a52bSMark Brown }; 425*a505a52bSMark Brown 426*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_gcs, s128k) 427*a505a52bSMark Brown { 428*a505a52bSMark Brown .stack_size = 128 * 1024, 429*a505a52bSMark Brown .flags = 0, 430*a505a52bSMark Brown }; 431*a505a52bSMark Brown 432*a505a52bSMark Brown FIXTURE_SETUP(map_gcs) 433*a505a52bSMark Brown { 434*a505a52bSMark Brown self->stack = (void *)syscall(__NR_map_shadow_stack, 0, 435*a505a52bSMark Brown variant->stack_size, 436*a505a52bSMark Brown variant->flags); 437*a505a52bSMark Brown ASSERT_FALSE(self->stack == MAP_FAILED); 438*a505a52bSMark Brown ksft_print_msg("Allocated stack from %p-%p\n", self->stack, 439*a505a52bSMark Brown self->stack + variant->stack_size); 440*a505a52bSMark Brown } 441*a505a52bSMark Brown 442*a505a52bSMark Brown FIXTURE_TEARDOWN(map_gcs) 443*a505a52bSMark Brown { 444*a505a52bSMark Brown int ret; 445*a505a52bSMark Brown 446*a505a52bSMark Brown if (self->stack != MAP_FAILED) { 447*a505a52bSMark Brown ret = munmap(self->stack, variant->stack_size); 448*a505a52bSMark Brown ASSERT_EQ(ret, 0); 449*a505a52bSMark Brown } 450*a505a52bSMark Brown } 451*a505a52bSMark Brown 452*a505a52bSMark Brown /* The stack has a cap token */ 453*a505a52bSMark Brown TEST_F(map_gcs, stack_capped) 454*a505a52bSMark Brown { 455*a505a52bSMark Brown unsigned long *stack = self->stack; 456*a505a52bSMark Brown size_t cap_index; 457*a505a52bSMark Brown 458*a505a52bSMark Brown cap_index = (variant->stack_size / sizeof(unsigned long)); 459*a505a52bSMark Brown 460*a505a52bSMark Brown switch (variant->flags & (SHADOW_STACK_SET_MARKER | SHADOW_STACK_SET_TOKEN)) { 461*a505a52bSMark Brown case SHADOW_STACK_SET_MARKER | SHADOW_STACK_SET_TOKEN: 462*a505a52bSMark Brown cap_index -= 2; 463*a505a52bSMark Brown break; 464*a505a52bSMark Brown case SHADOW_STACK_SET_TOKEN: 465*a505a52bSMark Brown cap_index -= 1; 466*a505a52bSMark Brown break; 467*a505a52bSMark Brown case SHADOW_STACK_SET_MARKER: 468*a505a52bSMark Brown case 0: 469*a505a52bSMark Brown /* No cap, no test */ 470*a505a52bSMark Brown return; 471*a505a52bSMark Brown } 472*a505a52bSMark Brown 473*a505a52bSMark Brown ASSERT_EQ(stack[cap_index], GCS_CAP(&stack[cap_index])); 474*a505a52bSMark Brown } 475*a505a52bSMark Brown 476*a505a52bSMark Brown /* The top of the stack is 0 */ 477*a505a52bSMark Brown TEST_F(map_gcs, stack_terminated) 478*a505a52bSMark Brown { 479*a505a52bSMark Brown unsigned long *stack = self->stack; 480*a505a52bSMark Brown size_t term_index; 481*a505a52bSMark Brown 482*a505a52bSMark Brown if (!(variant->flags & SHADOW_STACK_SET_MARKER)) 483*a505a52bSMark Brown return; 484*a505a52bSMark Brown 485*a505a52bSMark Brown term_index = (variant->stack_size / sizeof(unsigned long)) - 1; 486*a505a52bSMark Brown 487*a505a52bSMark Brown ASSERT_EQ(stack[term_index], 0); 488*a505a52bSMark Brown } 489*a505a52bSMark Brown 490*a505a52bSMark Brown /* Writes should fault */ 491*a505a52bSMark Brown TEST_F_SIGNAL(map_gcs, not_writeable, SIGSEGV) 492*a505a52bSMark Brown { 493*a505a52bSMark Brown self->stack[0] = 0; 494*a505a52bSMark Brown } 495*a505a52bSMark Brown 496*a505a52bSMark Brown /* Put it all together, we can safely switch to and from the stack */ 497*a505a52bSMark Brown TEST_F(map_gcs, stack_switch) 498*a505a52bSMark Brown { 499*a505a52bSMark Brown size_t cap_index; 500*a505a52bSMark Brown cap_index = (variant->stack_size / sizeof(unsigned long)); 501*a505a52bSMark Brown unsigned long *orig_gcspr_el0, *pivot_gcspr_el0; 502*a505a52bSMark Brown 503*a505a52bSMark Brown /* Skip over the stack terminator and point at the cap */ 504*a505a52bSMark Brown switch (variant->flags & (SHADOW_STACK_SET_MARKER | SHADOW_STACK_SET_TOKEN)) { 505*a505a52bSMark Brown case SHADOW_STACK_SET_MARKER | SHADOW_STACK_SET_TOKEN: 506*a505a52bSMark Brown cap_index -= 2; 507*a505a52bSMark Brown break; 508*a505a52bSMark Brown case SHADOW_STACK_SET_TOKEN: 509*a505a52bSMark Brown cap_index -= 1; 510*a505a52bSMark Brown break; 511*a505a52bSMark Brown case SHADOW_STACK_SET_MARKER: 512*a505a52bSMark Brown case 0: 513*a505a52bSMark Brown /* No cap, no test */ 514*a505a52bSMark Brown return; 515*a505a52bSMark Brown } 516*a505a52bSMark Brown pivot_gcspr_el0 = &self->stack[cap_index]; 517*a505a52bSMark Brown 518*a505a52bSMark Brown /* Pivot to the new GCS */ 519*a505a52bSMark Brown ksft_print_msg("Pivoting to %p from %p, target has value 0x%lx\n", 520*a505a52bSMark Brown pivot_gcspr_el0, get_gcspr(), 521*a505a52bSMark Brown *pivot_gcspr_el0); 522*a505a52bSMark Brown gcsss1(pivot_gcspr_el0); 523*a505a52bSMark Brown orig_gcspr_el0 = gcsss2(); 524*a505a52bSMark Brown ksft_print_msg("Pivoted to %p from %p, target has value 0x%lx\n", 525*a505a52bSMark Brown get_gcspr(), orig_gcspr_el0, 526*a505a52bSMark Brown *pivot_gcspr_el0); 527*a505a52bSMark Brown 528*a505a52bSMark Brown ksft_print_msg("Pivoted, GCSPR_EL0 now %p\n", get_gcspr()); 529*a505a52bSMark Brown 530*a505a52bSMark Brown /* New GCS must be in the new buffer */ 531*a505a52bSMark Brown ASSERT_TRUE((unsigned long)get_gcspr() > (unsigned long)self->stack); 532*a505a52bSMark Brown ASSERT_TRUE((unsigned long)get_gcspr() <= 533*a505a52bSMark Brown (unsigned long)self->stack + variant->stack_size); 534*a505a52bSMark Brown 535*a505a52bSMark Brown /* We should be able to use all but 2 slots of the new stack */ 536*a505a52bSMark Brown ksft_print_msg("Recursing %zu levels\n", cap_index - 1); 537*a505a52bSMark Brown gcs_recurse(cap_index - 1); 538*a505a52bSMark Brown 539*a505a52bSMark Brown /* Pivot back to the original GCS */ 540*a505a52bSMark Brown gcsss1(orig_gcspr_el0); 541*a505a52bSMark Brown pivot_gcspr_el0 = gcsss2(); 542*a505a52bSMark Brown 543*a505a52bSMark Brown gcs_recurse(0); 544*a505a52bSMark Brown ksft_print_msg("Pivoted back to GCSPR_EL0 0x%p\n", get_gcspr()); 545*a505a52bSMark Brown } 546*a505a52bSMark Brown 547*a505a52bSMark Brown /* We fault if we try to go beyond the end of the stack */ 548*a505a52bSMark Brown TEST_F_SIGNAL(map_gcs, stack_overflow, SIGSEGV) 549*a505a52bSMark Brown { 550*a505a52bSMark Brown size_t cap_index; 551*a505a52bSMark Brown cap_index = (variant->stack_size / sizeof(unsigned long)); 552*a505a52bSMark Brown unsigned long *orig_gcspr_el0, *pivot_gcspr_el0; 553*a505a52bSMark Brown 554*a505a52bSMark Brown /* Skip over the stack terminator and point at the cap */ 555*a505a52bSMark Brown switch (variant->flags & (SHADOW_STACK_SET_MARKER | SHADOW_STACK_SET_TOKEN)) { 556*a505a52bSMark Brown case SHADOW_STACK_SET_MARKER | SHADOW_STACK_SET_TOKEN: 557*a505a52bSMark Brown cap_index -= 2; 558*a505a52bSMark Brown break; 559*a505a52bSMark Brown case SHADOW_STACK_SET_TOKEN: 560*a505a52bSMark Brown cap_index -= 1; 561*a505a52bSMark Brown break; 562*a505a52bSMark Brown case SHADOW_STACK_SET_MARKER: 563*a505a52bSMark Brown case 0: 564*a505a52bSMark Brown /* No cap, no test but we need to SEGV to avoid a false fail */ 565*a505a52bSMark Brown orig_gcspr_el0 = get_gcspr(); 566*a505a52bSMark Brown *orig_gcspr_el0 = 0; 567*a505a52bSMark Brown return; 568*a505a52bSMark Brown } 569*a505a52bSMark Brown pivot_gcspr_el0 = &self->stack[cap_index]; 570*a505a52bSMark Brown 571*a505a52bSMark Brown /* Pivot to the new GCS */ 572*a505a52bSMark Brown ksft_print_msg("Pivoting to %p from %p, target has value 0x%lx\n", 573*a505a52bSMark Brown pivot_gcspr_el0, get_gcspr(), 574*a505a52bSMark Brown *pivot_gcspr_el0); 575*a505a52bSMark Brown gcsss1(pivot_gcspr_el0); 576*a505a52bSMark Brown orig_gcspr_el0 = gcsss2(); 577*a505a52bSMark Brown ksft_print_msg("Pivoted to %p from %p, target has value 0x%lx\n", 578*a505a52bSMark Brown pivot_gcspr_el0, orig_gcspr_el0, 579*a505a52bSMark Brown *pivot_gcspr_el0); 580*a505a52bSMark Brown 581*a505a52bSMark Brown ksft_print_msg("Pivoted, GCSPR_EL0 now %p\n", get_gcspr()); 582*a505a52bSMark Brown 583*a505a52bSMark Brown /* New GCS must be in the new buffer */ 584*a505a52bSMark Brown ASSERT_TRUE((unsigned long)get_gcspr() > (unsigned long)self->stack); 585*a505a52bSMark Brown ASSERT_TRUE((unsigned long)get_gcspr() <= 586*a505a52bSMark Brown (unsigned long)self->stack + variant->stack_size); 587*a505a52bSMark Brown 588*a505a52bSMark Brown /* Now try to recurse, we should fault doing this. */ 589*a505a52bSMark Brown ksft_print_msg("Recursing %zu levels...\n", cap_index + 1); 590*a505a52bSMark Brown gcs_recurse(cap_index + 1); 591*a505a52bSMark Brown ksft_print_msg("...done\n"); 592*a505a52bSMark Brown 593*a505a52bSMark Brown /* Clean up properly to try to guard against spurious passes. */ 594*a505a52bSMark Brown gcsss1(orig_gcspr_el0); 595*a505a52bSMark Brown pivot_gcspr_el0 = gcsss2(); 596*a505a52bSMark Brown ksft_print_msg("Pivoted back to GCSPR_EL0 0x%p\n", get_gcspr()); 597*a505a52bSMark Brown } 598*a505a52bSMark Brown 599*a505a52bSMark Brown FIXTURE(map_invalid_gcs) 600*a505a52bSMark Brown { 601*a505a52bSMark Brown }; 602*a505a52bSMark Brown 603*a505a52bSMark Brown FIXTURE_VARIANT(map_invalid_gcs) 604*a505a52bSMark Brown { 605*a505a52bSMark Brown size_t stack_size; 606*a505a52bSMark Brown }; 607*a505a52bSMark Brown 608*a505a52bSMark Brown FIXTURE_SETUP(map_invalid_gcs) 609*a505a52bSMark Brown { 610*a505a52bSMark Brown } 611*a505a52bSMark Brown 612*a505a52bSMark Brown FIXTURE_TEARDOWN(map_invalid_gcs) 613*a505a52bSMark Brown { 614*a505a52bSMark Brown } 615*a505a52bSMark Brown 616*a505a52bSMark Brown /* GCS must be larger than 16 bytes */ 617*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_invalid_gcs, too_small) 618*a505a52bSMark Brown { 619*a505a52bSMark Brown .stack_size = 8, 620*a505a52bSMark Brown }; 621*a505a52bSMark Brown 622*a505a52bSMark Brown /* GCS size must be 16 byte aligned */ 623*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_invalid_gcs, unligned_1) { .stack_size = 1024 + 1 }; 624*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_invalid_gcs, unligned_2) { .stack_size = 1024 + 2 }; 625*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_invalid_gcs, unligned_3) { .stack_size = 1024 + 3 }; 626*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_invalid_gcs, unligned_4) { .stack_size = 1024 + 4 }; 627*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_invalid_gcs, unligned_5) { .stack_size = 1024 + 5 }; 628*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_invalid_gcs, unligned_6) { .stack_size = 1024 + 6 }; 629*a505a52bSMark Brown FIXTURE_VARIANT_ADD(map_invalid_gcs, unligned_7) { .stack_size = 1024 + 7 }; 630*a505a52bSMark Brown 631*a505a52bSMark Brown TEST_F(map_invalid_gcs, do_map) 632*a505a52bSMark Brown { 633*a505a52bSMark Brown void *stack; 634*a505a52bSMark Brown 635*a505a52bSMark Brown stack = (void *)syscall(__NR_map_shadow_stack, 0, 636*a505a52bSMark Brown variant->stack_size, 0); 637*a505a52bSMark Brown ASSERT_TRUE(stack == MAP_FAILED); 638*a505a52bSMark Brown if (stack != MAP_FAILED) 639*a505a52bSMark Brown munmap(stack, variant->stack_size); 640*a505a52bSMark Brown } 641*a505a52bSMark Brown 642*a505a52bSMark Brown FIXTURE(invalid_mprotect) 643*a505a52bSMark Brown { 644*a505a52bSMark Brown unsigned long *stack; 645*a505a52bSMark Brown size_t stack_size; 646*a505a52bSMark Brown }; 647*a505a52bSMark Brown 648*a505a52bSMark Brown FIXTURE_VARIANT(invalid_mprotect) 649*a505a52bSMark Brown { 650*a505a52bSMark Brown unsigned long flags; 651*a505a52bSMark Brown }; 652*a505a52bSMark Brown 653*a505a52bSMark Brown FIXTURE_SETUP(invalid_mprotect) 654*a505a52bSMark Brown { 655*a505a52bSMark Brown self->stack_size = sysconf(_SC_PAGE_SIZE); 656*a505a52bSMark Brown self->stack = (void *)syscall(__NR_map_shadow_stack, 0, 657*a505a52bSMark Brown self->stack_size, 0); 658*a505a52bSMark Brown ASSERT_FALSE(self->stack == MAP_FAILED); 659*a505a52bSMark Brown ksft_print_msg("Allocated stack from %p-%p\n", self->stack, 660*a505a52bSMark Brown self->stack + self->stack_size); 661*a505a52bSMark Brown } 662*a505a52bSMark Brown 663*a505a52bSMark Brown FIXTURE_TEARDOWN(invalid_mprotect) 664*a505a52bSMark Brown { 665*a505a52bSMark Brown int ret; 666*a505a52bSMark Brown 667*a505a52bSMark Brown if (self->stack != MAP_FAILED) { 668*a505a52bSMark Brown ret = munmap(self->stack, self->stack_size); 669*a505a52bSMark Brown ASSERT_EQ(ret, 0); 670*a505a52bSMark Brown } 671*a505a52bSMark Brown } 672*a505a52bSMark Brown 673*a505a52bSMark Brown FIXTURE_VARIANT_ADD(invalid_mprotect, exec) 674*a505a52bSMark Brown { 675*a505a52bSMark Brown .flags = PROT_EXEC, 676*a505a52bSMark Brown }; 677*a505a52bSMark Brown 678*a505a52bSMark Brown TEST_F(invalid_mprotect, do_map) 679*a505a52bSMark Brown { 680*a505a52bSMark Brown int ret; 681*a505a52bSMark Brown 682*a505a52bSMark Brown ret = mprotect(self->stack, self->stack_size, variant->flags); 683*a505a52bSMark Brown ASSERT_EQ(ret, -1); 684*a505a52bSMark Brown } 685*a505a52bSMark Brown 686*a505a52bSMark Brown TEST_F(invalid_mprotect, do_map_read) 687*a505a52bSMark Brown { 688*a505a52bSMark Brown int ret; 689*a505a52bSMark Brown 690*a505a52bSMark Brown ret = mprotect(self->stack, self->stack_size, 691*a505a52bSMark Brown variant->flags | PROT_READ); 692*a505a52bSMark Brown ASSERT_EQ(ret, -1); 693*a505a52bSMark Brown } 694*a505a52bSMark Brown 695*a505a52bSMark Brown int main(int argc, char **argv) 696*a505a52bSMark Brown { 697*a505a52bSMark Brown unsigned long gcs_mode; 698*a505a52bSMark Brown int ret; 699*a505a52bSMark Brown 700*a505a52bSMark Brown if (!(getauxval(AT_HWCAP) & HWCAP_GCS)) 701*a505a52bSMark Brown ksft_exit_skip("SKIP GCS not supported\n"); 702*a505a52bSMark Brown 703*a505a52bSMark Brown /* 704*a505a52bSMark Brown * Force shadow stacks on, our tests *should* be fine with or 705*a505a52bSMark Brown * without libc support and with or without this having ended 706*a505a52bSMark Brown * up tagged for GCS and enabled by the dynamic linker. We 707*a505a52bSMark Brown * can't use the libc prctl() function since we can't return 708*a505a52bSMark Brown * from enabling the stack. 709*a505a52bSMark Brown */ 710*a505a52bSMark Brown ret = my_syscall2(__NR_prctl, PR_GET_SHADOW_STACK_STATUS, &gcs_mode); 711*a505a52bSMark Brown if (ret) { 712*a505a52bSMark Brown ksft_print_msg("Failed to read GCS state: %d\n", ret); 713*a505a52bSMark Brown return EXIT_FAILURE; 714*a505a52bSMark Brown } 715*a505a52bSMark Brown 716*a505a52bSMark Brown if (!(gcs_mode & PR_SHADOW_STACK_ENABLE)) { 717*a505a52bSMark Brown gcs_mode = PR_SHADOW_STACK_ENABLE; 718*a505a52bSMark Brown ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, 719*a505a52bSMark Brown gcs_mode); 720*a505a52bSMark Brown if (ret) { 721*a505a52bSMark Brown ksft_print_msg("Failed to configure GCS: %d\n", ret); 722*a505a52bSMark Brown return EXIT_FAILURE; 723*a505a52bSMark Brown } 724*a505a52bSMark Brown } 725*a505a52bSMark Brown 726*a505a52bSMark Brown /* Avoid returning in case libc doesn't understand GCS */ 727*a505a52bSMark Brown exit(test_harness_run(argc, argv)); 728*a505a52bSMark Brown } 729