1*7470b5afSSamuel Holland // SPDX-License-Identifier: GPL-2.0-only 2*7470b5afSSamuel Holland 3*7470b5afSSamuel Holland #include <errno.h> 4*7470b5afSSamuel Holland #include <fcntl.h> 5*7470b5afSSamuel Holland #include <setjmp.h> 6*7470b5afSSamuel Holland #include <signal.h> 7*7470b5afSSamuel Holland #include <stdbool.h> 8*7470b5afSSamuel Holland #include <sys/prctl.h> 9*7470b5afSSamuel Holland #include <sys/wait.h> 10*7470b5afSSamuel Holland #include <unistd.h> 11*7470b5afSSamuel Holland 12*7470b5afSSamuel Holland #include "../../kselftest.h" 13*7470b5afSSamuel Holland 14*7470b5afSSamuel Holland #ifndef PR_PMLEN_SHIFT 15*7470b5afSSamuel Holland #define PR_PMLEN_SHIFT 24 16*7470b5afSSamuel Holland #endif 17*7470b5afSSamuel Holland #ifndef PR_PMLEN_MASK 18*7470b5afSSamuel Holland #define PR_PMLEN_MASK (0x7fUL << PR_PMLEN_SHIFT) 19*7470b5afSSamuel Holland #endif 20*7470b5afSSamuel Holland 21*7470b5afSSamuel Holland static int dev_zero; 22*7470b5afSSamuel Holland 23*7470b5afSSamuel Holland static int pipefd[2]; 24*7470b5afSSamuel Holland 25*7470b5afSSamuel Holland static sigjmp_buf jmpbuf; 26*7470b5afSSamuel Holland 27*7470b5afSSamuel Holland static void sigsegv_handler(int sig) 28*7470b5afSSamuel Holland { 29*7470b5afSSamuel Holland siglongjmp(jmpbuf, 1); 30*7470b5afSSamuel Holland } 31*7470b5afSSamuel Holland 32*7470b5afSSamuel Holland static int min_pmlen; 33*7470b5afSSamuel Holland static int max_pmlen; 34*7470b5afSSamuel Holland 35*7470b5afSSamuel Holland static inline bool valid_pmlen(int pmlen) 36*7470b5afSSamuel Holland { 37*7470b5afSSamuel Holland return pmlen == 0 || pmlen == 7 || pmlen == 16; 38*7470b5afSSamuel Holland } 39*7470b5afSSamuel Holland 40*7470b5afSSamuel Holland static void test_pmlen(void) 41*7470b5afSSamuel Holland { 42*7470b5afSSamuel Holland ksft_print_msg("Testing available PMLEN values\n"); 43*7470b5afSSamuel Holland 44*7470b5afSSamuel Holland for (int request = 0; request <= 16; request++) { 45*7470b5afSSamuel Holland int pmlen, ret; 46*7470b5afSSamuel Holland 47*7470b5afSSamuel Holland ret = prctl(PR_SET_TAGGED_ADDR_CTRL, request << PR_PMLEN_SHIFT, 0, 0, 0); 48*7470b5afSSamuel Holland if (ret) 49*7470b5afSSamuel Holland goto pr_set_error; 50*7470b5afSSamuel Holland 51*7470b5afSSamuel Holland ret = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0); 52*7470b5afSSamuel Holland ksft_test_result(ret >= 0, "PMLEN=%d PR_GET_TAGGED_ADDR_CTRL\n", request); 53*7470b5afSSamuel Holland if (ret < 0) 54*7470b5afSSamuel Holland goto pr_get_error; 55*7470b5afSSamuel Holland 56*7470b5afSSamuel Holland pmlen = (ret & PR_PMLEN_MASK) >> PR_PMLEN_SHIFT; 57*7470b5afSSamuel Holland ksft_test_result(pmlen >= request, "PMLEN=%d constraint\n", request); 58*7470b5afSSamuel Holland ksft_test_result(valid_pmlen(pmlen), "PMLEN=%d validity\n", request); 59*7470b5afSSamuel Holland 60*7470b5afSSamuel Holland if (min_pmlen == 0) 61*7470b5afSSamuel Holland min_pmlen = pmlen; 62*7470b5afSSamuel Holland if (max_pmlen < pmlen) 63*7470b5afSSamuel Holland max_pmlen = pmlen; 64*7470b5afSSamuel Holland 65*7470b5afSSamuel Holland continue; 66*7470b5afSSamuel Holland 67*7470b5afSSamuel Holland pr_set_error: 68*7470b5afSSamuel Holland ksft_test_result_skip("PMLEN=%d PR_GET_TAGGED_ADDR_CTRL\n", request); 69*7470b5afSSamuel Holland pr_get_error: 70*7470b5afSSamuel Holland ksft_test_result_skip("PMLEN=%d constraint\n", request); 71*7470b5afSSamuel Holland ksft_test_result_skip("PMLEN=%d validity\n", request); 72*7470b5afSSamuel Holland } 73*7470b5afSSamuel Holland 74*7470b5afSSamuel Holland if (max_pmlen == 0) 75*7470b5afSSamuel Holland ksft_exit_fail_msg("Failed to enable pointer masking\n"); 76*7470b5afSSamuel Holland } 77*7470b5afSSamuel Holland 78*7470b5afSSamuel Holland static int set_tagged_addr_ctrl(int pmlen, bool tagged_addr_abi) 79*7470b5afSSamuel Holland { 80*7470b5afSSamuel Holland int arg, ret; 81*7470b5afSSamuel Holland 82*7470b5afSSamuel Holland arg = pmlen << PR_PMLEN_SHIFT | tagged_addr_abi; 83*7470b5afSSamuel Holland ret = prctl(PR_SET_TAGGED_ADDR_CTRL, arg, 0, 0, 0); 84*7470b5afSSamuel Holland if (!ret) { 85*7470b5afSSamuel Holland ret = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0); 86*7470b5afSSamuel Holland if (ret == arg) 87*7470b5afSSamuel Holland return 0; 88*7470b5afSSamuel Holland } 89*7470b5afSSamuel Holland 90*7470b5afSSamuel Holland return ret < 0 ? -errno : -ENODATA; 91*7470b5afSSamuel Holland } 92*7470b5afSSamuel Holland 93*7470b5afSSamuel Holland static void test_dereference_pmlen(int pmlen) 94*7470b5afSSamuel Holland { 95*7470b5afSSamuel Holland static volatile int i; 96*7470b5afSSamuel Holland volatile int *p; 97*7470b5afSSamuel Holland int ret; 98*7470b5afSSamuel Holland 99*7470b5afSSamuel Holland ret = set_tagged_addr_ctrl(pmlen, false); 100*7470b5afSSamuel Holland if (ret) 101*7470b5afSSamuel Holland return ksft_test_result_error("PMLEN=%d setup (%d)\n", pmlen, ret); 102*7470b5afSSamuel Holland 103*7470b5afSSamuel Holland i = pmlen; 104*7470b5afSSamuel Holland 105*7470b5afSSamuel Holland if (pmlen) { 106*7470b5afSSamuel Holland p = (volatile int *)((uintptr_t)&i | 1UL << (__riscv_xlen - pmlen)); 107*7470b5afSSamuel Holland 108*7470b5afSSamuel Holland /* These dereferences should succeed. */ 109*7470b5afSSamuel Holland if (sigsetjmp(jmpbuf, 1)) 110*7470b5afSSamuel Holland return ksft_test_result_fail("PMLEN=%d valid tag\n", pmlen); 111*7470b5afSSamuel Holland if (*p != pmlen) 112*7470b5afSSamuel Holland return ksft_test_result_fail("PMLEN=%d bad value\n", pmlen); 113*7470b5afSSamuel Holland ++*p; 114*7470b5afSSamuel Holland } 115*7470b5afSSamuel Holland 116*7470b5afSSamuel Holland p = (volatile int *)((uintptr_t)&i | 1UL << (__riscv_xlen - pmlen - 1)); 117*7470b5afSSamuel Holland 118*7470b5afSSamuel Holland /* These dereferences should raise SIGSEGV. */ 119*7470b5afSSamuel Holland if (sigsetjmp(jmpbuf, 1)) 120*7470b5afSSamuel Holland return ksft_test_result_pass("PMLEN=%d dereference\n", pmlen); 121*7470b5afSSamuel Holland ++*p; 122*7470b5afSSamuel Holland ksft_test_result_fail("PMLEN=%d invalid tag\n", pmlen); 123*7470b5afSSamuel Holland } 124*7470b5afSSamuel Holland 125*7470b5afSSamuel Holland static void test_dereference(void) 126*7470b5afSSamuel Holland { 127*7470b5afSSamuel Holland ksft_print_msg("Testing userspace pointer dereference\n"); 128*7470b5afSSamuel Holland 129*7470b5afSSamuel Holland signal(SIGSEGV, sigsegv_handler); 130*7470b5afSSamuel Holland 131*7470b5afSSamuel Holland test_dereference_pmlen(0); 132*7470b5afSSamuel Holland test_dereference_pmlen(min_pmlen); 133*7470b5afSSamuel Holland test_dereference_pmlen(max_pmlen); 134*7470b5afSSamuel Holland 135*7470b5afSSamuel Holland signal(SIGSEGV, SIG_DFL); 136*7470b5afSSamuel Holland } 137*7470b5afSSamuel Holland 138*7470b5afSSamuel Holland static void execve_child_sigsegv_handler(int sig) 139*7470b5afSSamuel Holland { 140*7470b5afSSamuel Holland exit(42); 141*7470b5afSSamuel Holland } 142*7470b5afSSamuel Holland 143*7470b5afSSamuel Holland static int execve_child(void) 144*7470b5afSSamuel Holland { 145*7470b5afSSamuel Holland static volatile int i; 146*7470b5afSSamuel Holland volatile int *p = (volatile int *)((uintptr_t)&i | 1UL << (__riscv_xlen - 7)); 147*7470b5afSSamuel Holland 148*7470b5afSSamuel Holland signal(SIGSEGV, execve_child_sigsegv_handler); 149*7470b5afSSamuel Holland 150*7470b5afSSamuel Holland /* This dereference should raise SIGSEGV. */ 151*7470b5afSSamuel Holland return *p; 152*7470b5afSSamuel Holland } 153*7470b5afSSamuel Holland 154*7470b5afSSamuel Holland static void test_fork_exec(void) 155*7470b5afSSamuel Holland { 156*7470b5afSSamuel Holland int ret, status; 157*7470b5afSSamuel Holland 158*7470b5afSSamuel Holland ksft_print_msg("Testing fork/exec behavior\n"); 159*7470b5afSSamuel Holland 160*7470b5afSSamuel Holland ret = set_tagged_addr_ctrl(min_pmlen, false); 161*7470b5afSSamuel Holland if (ret) 162*7470b5afSSamuel Holland return ksft_test_result_error("setup (%d)\n", ret); 163*7470b5afSSamuel Holland 164*7470b5afSSamuel Holland if (fork()) { 165*7470b5afSSamuel Holland wait(&status); 166*7470b5afSSamuel Holland ksft_test_result(WIFEXITED(status) && WEXITSTATUS(status) == 42, 167*7470b5afSSamuel Holland "dereference after fork\n"); 168*7470b5afSSamuel Holland } else { 169*7470b5afSSamuel Holland static volatile int i = 42; 170*7470b5afSSamuel Holland volatile int *p; 171*7470b5afSSamuel Holland 172*7470b5afSSamuel Holland p = (volatile int *)((uintptr_t)&i | 1UL << (__riscv_xlen - min_pmlen)); 173*7470b5afSSamuel Holland 174*7470b5afSSamuel Holland /* This dereference should succeed. */ 175*7470b5afSSamuel Holland exit(*p); 176*7470b5afSSamuel Holland } 177*7470b5afSSamuel Holland 178*7470b5afSSamuel Holland if (fork()) { 179*7470b5afSSamuel Holland wait(&status); 180*7470b5afSSamuel Holland ksft_test_result(WIFEXITED(status) && WEXITSTATUS(status) == 42, 181*7470b5afSSamuel Holland "dereference after fork+exec\n"); 182*7470b5afSSamuel Holland } else { 183*7470b5afSSamuel Holland /* Will call execve_child(). */ 184*7470b5afSSamuel Holland execve("/proc/self/exe", (char *const []) { "", NULL }, NULL); 185*7470b5afSSamuel Holland } 186*7470b5afSSamuel Holland } 187*7470b5afSSamuel Holland 188*7470b5afSSamuel Holland static void test_tagged_addr_abi_sysctl(void) 189*7470b5afSSamuel Holland { 190*7470b5afSSamuel Holland char value; 191*7470b5afSSamuel Holland int fd; 192*7470b5afSSamuel Holland 193*7470b5afSSamuel Holland ksft_print_msg("Testing tagged address ABI sysctl\n"); 194*7470b5afSSamuel Holland 195*7470b5afSSamuel Holland fd = open("/proc/sys/abi/tagged_addr_disabled", O_WRONLY); 196*7470b5afSSamuel Holland if (fd < 0) { 197*7470b5afSSamuel Holland ksft_test_result_skip("failed to open sysctl file\n"); 198*7470b5afSSamuel Holland ksft_test_result_skip("failed to open sysctl file\n"); 199*7470b5afSSamuel Holland return; 200*7470b5afSSamuel Holland } 201*7470b5afSSamuel Holland 202*7470b5afSSamuel Holland value = '1'; 203*7470b5afSSamuel Holland pwrite(fd, &value, 1, 0); 204*7470b5afSSamuel Holland ksft_test_result(set_tagged_addr_ctrl(min_pmlen, true) == -EINVAL, 205*7470b5afSSamuel Holland "sysctl disabled\n"); 206*7470b5afSSamuel Holland 207*7470b5afSSamuel Holland value = '0'; 208*7470b5afSSamuel Holland pwrite(fd, &value, 1, 0); 209*7470b5afSSamuel Holland ksft_test_result(set_tagged_addr_ctrl(min_pmlen, true) == 0, 210*7470b5afSSamuel Holland "sysctl enabled\n"); 211*7470b5afSSamuel Holland 212*7470b5afSSamuel Holland set_tagged_addr_ctrl(0, false); 213*7470b5afSSamuel Holland 214*7470b5afSSamuel Holland close(fd); 215*7470b5afSSamuel Holland } 216*7470b5afSSamuel Holland 217*7470b5afSSamuel Holland static void test_tagged_addr_abi_pmlen(int pmlen) 218*7470b5afSSamuel Holland { 219*7470b5afSSamuel Holland int i, *p, ret; 220*7470b5afSSamuel Holland 221*7470b5afSSamuel Holland i = ~pmlen; 222*7470b5afSSamuel Holland 223*7470b5afSSamuel Holland if (pmlen) { 224*7470b5afSSamuel Holland p = (int *)((uintptr_t)&i | 1UL << (__riscv_xlen - pmlen)); 225*7470b5afSSamuel Holland 226*7470b5afSSamuel Holland ret = set_tagged_addr_ctrl(pmlen, false); 227*7470b5afSSamuel Holland if (ret) 228*7470b5afSSamuel Holland return ksft_test_result_error("PMLEN=%d ABI disabled setup (%d)\n", 229*7470b5afSSamuel Holland pmlen, ret); 230*7470b5afSSamuel Holland 231*7470b5afSSamuel Holland ret = write(pipefd[1], p, sizeof(*p)); 232*7470b5afSSamuel Holland if (ret >= 0 || errno != EFAULT) 233*7470b5afSSamuel Holland return ksft_test_result_fail("PMLEN=%d ABI disabled write\n", pmlen); 234*7470b5afSSamuel Holland 235*7470b5afSSamuel Holland ret = read(dev_zero, p, sizeof(*p)); 236*7470b5afSSamuel Holland if (ret >= 0 || errno != EFAULT) 237*7470b5afSSamuel Holland return ksft_test_result_fail("PMLEN=%d ABI disabled read\n", pmlen); 238*7470b5afSSamuel Holland 239*7470b5afSSamuel Holland if (i != ~pmlen) 240*7470b5afSSamuel Holland return ksft_test_result_fail("PMLEN=%d ABI disabled value\n", pmlen); 241*7470b5afSSamuel Holland 242*7470b5afSSamuel Holland ret = set_tagged_addr_ctrl(pmlen, true); 243*7470b5afSSamuel Holland if (ret) 244*7470b5afSSamuel Holland return ksft_test_result_error("PMLEN=%d ABI enabled setup (%d)\n", 245*7470b5afSSamuel Holland pmlen, ret); 246*7470b5afSSamuel Holland 247*7470b5afSSamuel Holland ret = write(pipefd[1], p, sizeof(*p)); 248*7470b5afSSamuel Holland if (ret != sizeof(*p)) 249*7470b5afSSamuel Holland return ksft_test_result_fail("PMLEN=%d ABI enabled write\n", pmlen); 250*7470b5afSSamuel Holland 251*7470b5afSSamuel Holland ret = read(dev_zero, p, sizeof(*p)); 252*7470b5afSSamuel Holland if (ret != sizeof(*p)) 253*7470b5afSSamuel Holland return ksft_test_result_fail("PMLEN=%d ABI enabled read\n", pmlen); 254*7470b5afSSamuel Holland 255*7470b5afSSamuel Holland if (i) 256*7470b5afSSamuel Holland return ksft_test_result_fail("PMLEN=%d ABI enabled value\n", pmlen); 257*7470b5afSSamuel Holland 258*7470b5afSSamuel Holland i = ~pmlen; 259*7470b5afSSamuel Holland } else { 260*7470b5afSSamuel Holland /* The tagged address ABI cannot be enabled when PMLEN == 0. */ 261*7470b5afSSamuel Holland ret = set_tagged_addr_ctrl(pmlen, true); 262*7470b5afSSamuel Holland if (ret != -EINVAL) 263*7470b5afSSamuel Holland return ksft_test_result_error("PMLEN=%d ABI setup (%d)\n", 264*7470b5afSSamuel Holland pmlen, ret); 265*7470b5afSSamuel Holland } 266*7470b5afSSamuel Holland 267*7470b5afSSamuel Holland p = (int *)((uintptr_t)&i | 1UL << (__riscv_xlen - pmlen - 1)); 268*7470b5afSSamuel Holland 269*7470b5afSSamuel Holland ret = write(pipefd[1], p, sizeof(*p)); 270*7470b5afSSamuel Holland if (ret >= 0 || errno != EFAULT) 271*7470b5afSSamuel Holland return ksft_test_result_fail("PMLEN=%d invalid tag write (%d)\n", pmlen, errno); 272*7470b5afSSamuel Holland 273*7470b5afSSamuel Holland ret = read(dev_zero, p, sizeof(*p)); 274*7470b5afSSamuel Holland if (ret >= 0 || errno != EFAULT) 275*7470b5afSSamuel Holland return ksft_test_result_fail("PMLEN=%d invalid tag read\n", pmlen); 276*7470b5afSSamuel Holland 277*7470b5afSSamuel Holland if (i != ~pmlen) 278*7470b5afSSamuel Holland return ksft_test_result_fail("PMLEN=%d invalid tag value\n", pmlen); 279*7470b5afSSamuel Holland 280*7470b5afSSamuel Holland ksft_test_result_pass("PMLEN=%d tagged address ABI\n", pmlen); 281*7470b5afSSamuel Holland } 282*7470b5afSSamuel Holland 283*7470b5afSSamuel Holland static void test_tagged_addr_abi(void) 284*7470b5afSSamuel Holland { 285*7470b5afSSamuel Holland ksft_print_msg("Testing tagged address ABI\n"); 286*7470b5afSSamuel Holland 287*7470b5afSSamuel Holland test_tagged_addr_abi_pmlen(0); 288*7470b5afSSamuel Holland test_tagged_addr_abi_pmlen(min_pmlen); 289*7470b5afSSamuel Holland test_tagged_addr_abi_pmlen(max_pmlen); 290*7470b5afSSamuel Holland } 291*7470b5afSSamuel Holland 292*7470b5afSSamuel Holland static struct test_info { 293*7470b5afSSamuel Holland unsigned int nr_tests; 294*7470b5afSSamuel Holland void (*test_fn)(void); 295*7470b5afSSamuel Holland } tests[] = { 296*7470b5afSSamuel Holland { .nr_tests = 17 * 3, test_pmlen }, 297*7470b5afSSamuel Holland { .nr_tests = 3, test_dereference }, 298*7470b5afSSamuel Holland { .nr_tests = 2, test_fork_exec }, 299*7470b5afSSamuel Holland { .nr_tests = 2, test_tagged_addr_abi_sysctl }, 300*7470b5afSSamuel Holland { .nr_tests = 3, test_tagged_addr_abi }, 301*7470b5afSSamuel Holland }; 302*7470b5afSSamuel Holland 303*7470b5afSSamuel Holland int main(int argc, char **argv) 304*7470b5afSSamuel Holland { 305*7470b5afSSamuel Holland unsigned int plan = 0; 306*7470b5afSSamuel Holland int ret; 307*7470b5afSSamuel Holland 308*7470b5afSSamuel Holland /* Check if this is the child process after execve(). */ 309*7470b5afSSamuel Holland if (!argv[0][0]) 310*7470b5afSSamuel Holland return execve_child(); 311*7470b5afSSamuel Holland 312*7470b5afSSamuel Holland dev_zero = open("/dev/zero", O_RDWR); 313*7470b5afSSamuel Holland if (dev_zero < 0) 314*7470b5afSSamuel Holland return 1; 315*7470b5afSSamuel Holland 316*7470b5afSSamuel Holland /* Write to a pipe so the kernel must dereference the buffer pointer. */ 317*7470b5afSSamuel Holland ret = pipe(pipefd); 318*7470b5afSSamuel Holland if (ret) 319*7470b5afSSamuel Holland return 1; 320*7470b5afSSamuel Holland 321*7470b5afSSamuel Holland ksft_print_header(); 322*7470b5afSSamuel Holland 323*7470b5afSSamuel Holland for (int i = 0; i < ARRAY_SIZE(tests); i++) 324*7470b5afSSamuel Holland plan += tests[i].nr_tests; 325*7470b5afSSamuel Holland 326*7470b5afSSamuel Holland ksft_set_plan(plan); 327*7470b5afSSamuel Holland 328*7470b5afSSamuel Holland for (int i = 0; i < ARRAY_SIZE(tests); i++) 329*7470b5afSSamuel Holland tests[i].test_fn(); 330*7470b5afSSamuel Holland 331*7470b5afSSamuel Holland ksft_finished(); 332*7470b5afSSamuel Holland } 333