1*58d69a3eSMark Brown // SPDX-License-Identifier: GPL-2.0-only 2*58d69a3eSMark Brown /* 3*58d69a3eSMark Brown * Copyright (C) 2023 ARM Limited. 4*58d69a3eSMark Brown * 5*58d69a3eSMark Brown * Tests for GCS mode locking. These tests rely on both having GCS 6*58d69a3eSMark Brown * unconfigured on entry and on the kselftest harness running each 7*58d69a3eSMark Brown * test in a fork()ed process which will have it's own mode. 8*58d69a3eSMark Brown */ 9*58d69a3eSMark Brown 10*58d69a3eSMark Brown #include <limits.h> 11*58d69a3eSMark Brown 12*58d69a3eSMark Brown #include <sys/auxv.h> 13*58d69a3eSMark Brown #include <sys/prctl.h> 14*58d69a3eSMark Brown 15*58d69a3eSMark Brown #include <asm/hwcap.h> 16*58d69a3eSMark Brown 17*58d69a3eSMark Brown #include "kselftest_harness.h" 18*58d69a3eSMark Brown 19*58d69a3eSMark Brown #include "gcs-util.h" 20*58d69a3eSMark Brown 21*58d69a3eSMark Brown #define my_syscall2(num, arg1, arg2) \ 22*58d69a3eSMark Brown ({ \ 23*58d69a3eSMark Brown register long _num __asm__ ("x8") = (num); \ 24*58d69a3eSMark Brown register long _arg1 __asm__ ("x0") = (long)(arg1); \ 25*58d69a3eSMark Brown register long _arg2 __asm__ ("x1") = (long)(arg2); \ 26*58d69a3eSMark Brown register long _arg3 __asm__ ("x2") = 0; \ 27*58d69a3eSMark Brown register long _arg4 __asm__ ("x3") = 0; \ 28*58d69a3eSMark Brown register long _arg5 __asm__ ("x4") = 0; \ 29*58d69a3eSMark Brown \ 30*58d69a3eSMark Brown __asm__ volatile ( \ 31*58d69a3eSMark Brown "svc #0\n" \ 32*58d69a3eSMark Brown : "=r"(_arg1) \ 33*58d69a3eSMark Brown : "r"(_arg1), "r"(_arg2), \ 34*58d69a3eSMark Brown "r"(_arg3), "r"(_arg4), \ 35*58d69a3eSMark Brown "r"(_arg5), "r"(_num) \ 36*58d69a3eSMark Brown : "memory", "cc" \ 37*58d69a3eSMark Brown ); \ 38*58d69a3eSMark Brown _arg1; \ 39*58d69a3eSMark Brown }) 40*58d69a3eSMark Brown 41*58d69a3eSMark Brown /* No mode bits are rejected for locking */ 42*58d69a3eSMark Brown TEST(lock_all_modes) 43*58d69a3eSMark Brown { 44*58d69a3eSMark Brown int ret; 45*58d69a3eSMark Brown 46*58d69a3eSMark Brown ret = prctl(PR_LOCK_SHADOW_STACK_STATUS, ULONG_MAX, 0, 0, 0); 47*58d69a3eSMark Brown ASSERT_EQ(ret, 0); 48*58d69a3eSMark Brown } 49*58d69a3eSMark Brown 50*58d69a3eSMark Brown FIXTURE(valid_modes) 51*58d69a3eSMark Brown { 52*58d69a3eSMark Brown }; 53*58d69a3eSMark Brown 54*58d69a3eSMark Brown FIXTURE_VARIANT(valid_modes) 55*58d69a3eSMark Brown { 56*58d69a3eSMark Brown unsigned long mode; 57*58d69a3eSMark Brown }; 58*58d69a3eSMark Brown 59*58d69a3eSMark Brown FIXTURE_VARIANT_ADD(valid_modes, enable) 60*58d69a3eSMark Brown { 61*58d69a3eSMark Brown .mode = PR_SHADOW_STACK_ENABLE, 62*58d69a3eSMark Brown }; 63*58d69a3eSMark Brown 64*58d69a3eSMark Brown FIXTURE_VARIANT_ADD(valid_modes, enable_write) 65*58d69a3eSMark Brown { 66*58d69a3eSMark Brown .mode = PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_WRITE, 67*58d69a3eSMark Brown }; 68*58d69a3eSMark Brown 69*58d69a3eSMark Brown FIXTURE_VARIANT_ADD(valid_modes, enable_push) 70*58d69a3eSMark Brown { 71*58d69a3eSMark Brown .mode = PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_PUSH, 72*58d69a3eSMark Brown }; 73*58d69a3eSMark Brown 74*58d69a3eSMark Brown FIXTURE_VARIANT_ADD(valid_modes, enable_write_push) 75*58d69a3eSMark Brown { 76*58d69a3eSMark Brown .mode = PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_WRITE | 77*58d69a3eSMark Brown PR_SHADOW_STACK_PUSH, 78*58d69a3eSMark Brown }; 79*58d69a3eSMark Brown 80*58d69a3eSMark Brown FIXTURE_SETUP(valid_modes) 81*58d69a3eSMark Brown { 82*58d69a3eSMark Brown } 83*58d69a3eSMark Brown 84*58d69a3eSMark Brown FIXTURE_TEARDOWN(valid_modes) 85*58d69a3eSMark Brown { 86*58d69a3eSMark Brown } 87*58d69a3eSMark Brown 88*58d69a3eSMark Brown /* We can set the mode at all */ 89*58d69a3eSMark Brown TEST_F(valid_modes, set) 90*58d69a3eSMark Brown { 91*58d69a3eSMark Brown int ret; 92*58d69a3eSMark Brown 93*58d69a3eSMark Brown ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, 94*58d69a3eSMark Brown variant->mode); 95*58d69a3eSMark Brown ASSERT_EQ(ret, 0); 96*58d69a3eSMark Brown 97*58d69a3eSMark Brown _exit(0); 98*58d69a3eSMark Brown } 99*58d69a3eSMark Brown 100*58d69a3eSMark Brown /* Enabling, locking then disabling is rejected */ 101*58d69a3eSMark Brown TEST_F(valid_modes, enable_lock_disable) 102*58d69a3eSMark Brown { 103*58d69a3eSMark Brown unsigned long mode; 104*58d69a3eSMark Brown int ret; 105*58d69a3eSMark Brown 106*58d69a3eSMark Brown ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, 107*58d69a3eSMark Brown variant->mode); 108*58d69a3eSMark Brown ASSERT_EQ(ret, 0); 109*58d69a3eSMark Brown 110*58d69a3eSMark Brown ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0); 111*58d69a3eSMark Brown ASSERT_EQ(ret, 0); 112*58d69a3eSMark Brown ASSERT_EQ(mode, variant->mode); 113*58d69a3eSMark Brown 114*58d69a3eSMark Brown ret = prctl(PR_LOCK_SHADOW_STACK_STATUS, variant->mode, 0, 0, 0); 115*58d69a3eSMark Brown ASSERT_EQ(ret, 0); 116*58d69a3eSMark Brown 117*58d69a3eSMark Brown ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, 0); 118*58d69a3eSMark Brown ASSERT_EQ(ret, -EBUSY); 119*58d69a3eSMark Brown 120*58d69a3eSMark Brown _exit(0); 121*58d69a3eSMark Brown } 122*58d69a3eSMark Brown 123*58d69a3eSMark Brown /* Locking then enabling is rejected */ 124*58d69a3eSMark Brown TEST_F(valid_modes, lock_enable) 125*58d69a3eSMark Brown { 126*58d69a3eSMark Brown unsigned long mode; 127*58d69a3eSMark Brown int ret; 128*58d69a3eSMark Brown 129*58d69a3eSMark Brown ret = prctl(PR_LOCK_SHADOW_STACK_STATUS, variant->mode, 0, 0, 0); 130*58d69a3eSMark Brown ASSERT_EQ(ret, 0); 131*58d69a3eSMark Brown 132*58d69a3eSMark Brown ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, 133*58d69a3eSMark Brown variant->mode); 134*58d69a3eSMark Brown ASSERT_EQ(ret, -EBUSY); 135*58d69a3eSMark Brown 136*58d69a3eSMark Brown ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0); 137*58d69a3eSMark Brown ASSERT_EQ(ret, 0); 138*58d69a3eSMark Brown ASSERT_EQ(mode, 0); 139*58d69a3eSMark Brown 140*58d69a3eSMark Brown _exit(0); 141*58d69a3eSMark Brown } 142*58d69a3eSMark Brown 143*58d69a3eSMark Brown /* Locking then changing other modes is fine */ 144*58d69a3eSMark Brown TEST_F(valid_modes, lock_enable_disable_others) 145*58d69a3eSMark Brown { 146*58d69a3eSMark Brown unsigned long mode; 147*58d69a3eSMark Brown int ret; 148*58d69a3eSMark Brown 149*58d69a3eSMark Brown ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, 150*58d69a3eSMark Brown variant->mode); 151*58d69a3eSMark Brown ASSERT_EQ(ret, 0); 152*58d69a3eSMark Brown 153*58d69a3eSMark Brown ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0); 154*58d69a3eSMark Brown ASSERT_EQ(ret, 0); 155*58d69a3eSMark Brown ASSERT_EQ(mode, variant->mode); 156*58d69a3eSMark Brown 157*58d69a3eSMark Brown ret = prctl(PR_LOCK_SHADOW_STACK_STATUS, variant->mode, 0, 0, 0); 158*58d69a3eSMark Brown ASSERT_EQ(ret, 0); 159*58d69a3eSMark Brown 160*58d69a3eSMark Brown ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, 161*58d69a3eSMark Brown PR_SHADOW_STACK_ALL_MODES); 162*58d69a3eSMark Brown ASSERT_EQ(ret, 0); 163*58d69a3eSMark Brown 164*58d69a3eSMark Brown ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0); 165*58d69a3eSMark Brown ASSERT_EQ(ret, 0); 166*58d69a3eSMark Brown ASSERT_EQ(mode, PR_SHADOW_STACK_ALL_MODES); 167*58d69a3eSMark Brown 168*58d69a3eSMark Brown 169*58d69a3eSMark Brown ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, 170*58d69a3eSMark Brown variant->mode); 171*58d69a3eSMark Brown ASSERT_EQ(ret, 0); 172*58d69a3eSMark Brown 173*58d69a3eSMark Brown ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0); 174*58d69a3eSMark Brown ASSERT_EQ(ret, 0); 175*58d69a3eSMark Brown ASSERT_EQ(mode, variant->mode); 176*58d69a3eSMark Brown 177*58d69a3eSMark Brown _exit(0); 178*58d69a3eSMark Brown } 179*58d69a3eSMark Brown 180*58d69a3eSMark Brown int main(int argc, char **argv) 181*58d69a3eSMark Brown { 182*58d69a3eSMark Brown unsigned long mode; 183*58d69a3eSMark Brown int ret; 184*58d69a3eSMark Brown 185*58d69a3eSMark Brown if (!(getauxval(AT_HWCAP) & HWCAP_GCS)) 186*58d69a3eSMark Brown ksft_exit_skip("SKIP GCS not supported\n"); 187*58d69a3eSMark Brown 188*58d69a3eSMark Brown ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0); 189*58d69a3eSMark Brown if (ret) { 190*58d69a3eSMark Brown ksft_print_msg("Failed to read GCS state: %d\n", ret); 191*58d69a3eSMark Brown return EXIT_FAILURE; 192*58d69a3eSMark Brown } 193*58d69a3eSMark Brown 194*58d69a3eSMark Brown if (mode & PR_SHADOW_STACK_ENABLE) { 195*58d69a3eSMark Brown ksft_print_msg("GCS was enabled, test unsupported\n"); 196*58d69a3eSMark Brown return KSFT_SKIP; 197*58d69a3eSMark Brown } 198*58d69a3eSMark Brown 199*58d69a3eSMark Brown return test_harness_run(argc, argv); 200*58d69a3eSMark Brown } 201