17470b5afSSamuel Holland // SPDX-License-Identifier: GPL-2.0-only 27470b5afSSamuel Holland 37470b5afSSamuel Holland #include <errno.h> 47470b5afSSamuel Holland #include <fcntl.h> 57470b5afSSamuel Holland #include <setjmp.h> 67470b5afSSamuel Holland #include <signal.h> 77470b5afSSamuel Holland #include <stdbool.h> 87470b5afSSamuel Holland #include <sys/prctl.h> 97470b5afSSamuel Holland #include <sys/wait.h> 107470b5afSSamuel Holland #include <unistd.h> 117470b5afSSamuel Holland 127470b5afSSamuel Holland #include "../../kselftest.h" 137470b5afSSamuel Holland 147470b5afSSamuel Holland #ifndef PR_PMLEN_SHIFT 157470b5afSSamuel Holland #define PR_PMLEN_SHIFT 24 167470b5afSSamuel Holland #endif 177470b5afSSamuel Holland #ifndef PR_PMLEN_MASK 187470b5afSSamuel Holland #define PR_PMLEN_MASK (0x7fUL << PR_PMLEN_SHIFT) 197470b5afSSamuel Holland #endif 207470b5afSSamuel Holland 217470b5afSSamuel Holland static int dev_zero; 227470b5afSSamuel Holland 237470b5afSSamuel Holland static int pipefd[2]; 247470b5afSSamuel Holland 257470b5afSSamuel Holland static sigjmp_buf jmpbuf; 267470b5afSSamuel Holland 277470b5afSSamuel Holland static void sigsegv_handler(int sig) 287470b5afSSamuel Holland { 297470b5afSSamuel Holland siglongjmp(jmpbuf, 1); 307470b5afSSamuel Holland } 317470b5afSSamuel Holland 327470b5afSSamuel Holland static int min_pmlen; 337470b5afSSamuel Holland static int max_pmlen; 347470b5afSSamuel Holland 357470b5afSSamuel Holland static inline bool valid_pmlen(int pmlen) 367470b5afSSamuel Holland { 377470b5afSSamuel Holland return pmlen == 0 || pmlen == 7 || pmlen == 16; 387470b5afSSamuel Holland } 397470b5afSSamuel Holland 407470b5afSSamuel Holland static void test_pmlen(void) 417470b5afSSamuel Holland { 427470b5afSSamuel Holland ksft_print_msg("Testing available PMLEN values\n"); 437470b5afSSamuel Holland 447470b5afSSamuel Holland for (int request = 0; request <= 16; request++) { 457470b5afSSamuel Holland int pmlen, ret; 467470b5afSSamuel Holland 477470b5afSSamuel Holland ret = prctl(PR_SET_TAGGED_ADDR_CTRL, request << PR_PMLEN_SHIFT, 0, 0, 0); 487470b5afSSamuel Holland if (ret) 497470b5afSSamuel Holland goto pr_set_error; 507470b5afSSamuel Holland 517470b5afSSamuel Holland ret = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0); 527470b5afSSamuel Holland ksft_test_result(ret >= 0, "PMLEN=%d PR_GET_TAGGED_ADDR_CTRL\n", request); 537470b5afSSamuel Holland if (ret < 0) 547470b5afSSamuel Holland goto pr_get_error; 557470b5afSSamuel Holland 567470b5afSSamuel Holland pmlen = (ret & PR_PMLEN_MASK) >> PR_PMLEN_SHIFT; 577470b5afSSamuel Holland ksft_test_result(pmlen >= request, "PMLEN=%d constraint\n", request); 587470b5afSSamuel Holland ksft_test_result(valid_pmlen(pmlen), "PMLEN=%d validity\n", request); 597470b5afSSamuel Holland 607470b5afSSamuel Holland if (min_pmlen == 0) 617470b5afSSamuel Holland min_pmlen = pmlen; 627470b5afSSamuel Holland if (max_pmlen < pmlen) 637470b5afSSamuel Holland max_pmlen = pmlen; 647470b5afSSamuel Holland 657470b5afSSamuel Holland continue; 667470b5afSSamuel Holland 677470b5afSSamuel Holland pr_set_error: 687470b5afSSamuel Holland ksft_test_result_skip("PMLEN=%d PR_GET_TAGGED_ADDR_CTRL\n", request); 697470b5afSSamuel Holland pr_get_error: 707470b5afSSamuel Holland ksft_test_result_skip("PMLEN=%d constraint\n", request); 717470b5afSSamuel Holland ksft_test_result_skip("PMLEN=%d validity\n", request); 727470b5afSSamuel Holland } 737470b5afSSamuel Holland 747470b5afSSamuel Holland if (max_pmlen == 0) 757470b5afSSamuel Holland ksft_exit_fail_msg("Failed to enable pointer masking\n"); 767470b5afSSamuel Holland } 777470b5afSSamuel Holland 787470b5afSSamuel Holland static int set_tagged_addr_ctrl(int pmlen, bool tagged_addr_abi) 797470b5afSSamuel Holland { 807470b5afSSamuel Holland int arg, ret; 817470b5afSSamuel Holland 827470b5afSSamuel Holland arg = pmlen << PR_PMLEN_SHIFT | tagged_addr_abi; 837470b5afSSamuel Holland ret = prctl(PR_SET_TAGGED_ADDR_CTRL, arg, 0, 0, 0); 847470b5afSSamuel Holland if (!ret) { 857470b5afSSamuel Holland ret = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0); 867470b5afSSamuel Holland if (ret == arg) 877470b5afSSamuel Holland return 0; 887470b5afSSamuel Holland } 897470b5afSSamuel Holland 907470b5afSSamuel Holland return ret < 0 ? -errno : -ENODATA; 917470b5afSSamuel Holland } 927470b5afSSamuel Holland 937470b5afSSamuel Holland static void test_dereference_pmlen(int pmlen) 947470b5afSSamuel Holland { 957470b5afSSamuel Holland static volatile int i; 967470b5afSSamuel Holland volatile int *p; 977470b5afSSamuel Holland int ret; 987470b5afSSamuel Holland 997470b5afSSamuel Holland ret = set_tagged_addr_ctrl(pmlen, false); 1007470b5afSSamuel Holland if (ret) 1017470b5afSSamuel Holland return ksft_test_result_error("PMLEN=%d setup (%d)\n", pmlen, ret); 1027470b5afSSamuel Holland 1037470b5afSSamuel Holland i = pmlen; 1047470b5afSSamuel Holland 1057470b5afSSamuel Holland if (pmlen) { 1067470b5afSSamuel Holland p = (volatile int *)((uintptr_t)&i | 1UL << (__riscv_xlen - pmlen)); 1077470b5afSSamuel Holland 1087470b5afSSamuel Holland /* These dereferences should succeed. */ 1097470b5afSSamuel Holland if (sigsetjmp(jmpbuf, 1)) 1107470b5afSSamuel Holland return ksft_test_result_fail("PMLEN=%d valid tag\n", pmlen); 1117470b5afSSamuel Holland if (*p != pmlen) 1127470b5afSSamuel Holland return ksft_test_result_fail("PMLEN=%d bad value\n", pmlen); 1137470b5afSSamuel Holland ++*p; 1147470b5afSSamuel Holland } 1157470b5afSSamuel Holland 1167470b5afSSamuel Holland p = (volatile int *)((uintptr_t)&i | 1UL << (__riscv_xlen - pmlen - 1)); 1177470b5afSSamuel Holland 1187470b5afSSamuel Holland /* These dereferences should raise SIGSEGV. */ 1197470b5afSSamuel Holland if (sigsetjmp(jmpbuf, 1)) 1207470b5afSSamuel Holland return ksft_test_result_pass("PMLEN=%d dereference\n", pmlen); 1217470b5afSSamuel Holland ++*p; 1227470b5afSSamuel Holland ksft_test_result_fail("PMLEN=%d invalid tag\n", pmlen); 1237470b5afSSamuel Holland } 1247470b5afSSamuel Holland 1257470b5afSSamuel Holland static void test_dereference(void) 1267470b5afSSamuel Holland { 1277470b5afSSamuel Holland ksft_print_msg("Testing userspace pointer dereference\n"); 1287470b5afSSamuel Holland 1297470b5afSSamuel Holland signal(SIGSEGV, sigsegv_handler); 1307470b5afSSamuel Holland 1317470b5afSSamuel Holland test_dereference_pmlen(0); 1327470b5afSSamuel Holland test_dereference_pmlen(min_pmlen); 1337470b5afSSamuel Holland test_dereference_pmlen(max_pmlen); 1347470b5afSSamuel Holland 1357470b5afSSamuel Holland signal(SIGSEGV, SIG_DFL); 1367470b5afSSamuel Holland } 1377470b5afSSamuel Holland 1387470b5afSSamuel Holland static void execve_child_sigsegv_handler(int sig) 1397470b5afSSamuel Holland { 1407470b5afSSamuel Holland exit(42); 1417470b5afSSamuel Holland } 1427470b5afSSamuel Holland 1437470b5afSSamuel Holland static int execve_child(void) 1447470b5afSSamuel Holland { 1457470b5afSSamuel Holland static volatile int i; 1467470b5afSSamuel Holland volatile int *p = (volatile int *)((uintptr_t)&i | 1UL << (__riscv_xlen - 7)); 1477470b5afSSamuel Holland 1487470b5afSSamuel Holland signal(SIGSEGV, execve_child_sigsegv_handler); 1497470b5afSSamuel Holland 1507470b5afSSamuel Holland /* This dereference should raise SIGSEGV. */ 1517470b5afSSamuel Holland return *p; 1527470b5afSSamuel Holland } 1537470b5afSSamuel Holland 1547470b5afSSamuel Holland static void test_fork_exec(void) 1557470b5afSSamuel Holland { 1567470b5afSSamuel Holland int ret, status; 1577470b5afSSamuel Holland 1587470b5afSSamuel Holland ksft_print_msg("Testing fork/exec behavior\n"); 1597470b5afSSamuel Holland 1607470b5afSSamuel Holland ret = set_tagged_addr_ctrl(min_pmlen, false); 1617470b5afSSamuel Holland if (ret) 1627470b5afSSamuel Holland return ksft_test_result_error("setup (%d)\n", ret); 1637470b5afSSamuel Holland 1647470b5afSSamuel Holland if (fork()) { 1657470b5afSSamuel Holland wait(&status); 1667470b5afSSamuel Holland ksft_test_result(WIFEXITED(status) && WEXITSTATUS(status) == 42, 1677470b5afSSamuel Holland "dereference after fork\n"); 1687470b5afSSamuel Holland } else { 1697470b5afSSamuel Holland static volatile int i = 42; 1707470b5afSSamuel Holland volatile int *p; 1717470b5afSSamuel Holland 1727470b5afSSamuel Holland p = (volatile int *)((uintptr_t)&i | 1UL << (__riscv_xlen - min_pmlen)); 1737470b5afSSamuel Holland 1747470b5afSSamuel Holland /* This dereference should succeed. */ 1757470b5afSSamuel Holland exit(*p); 1767470b5afSSamuel Holland } 1777470b5afSSamuel Holland 1787470b5afSSamuel Holland if (fork()) { 1797470b5afSSamuel Holland wait(&status); 1807470b5afSSamuel Holland ksft_test_result(WIFEXITED(status) && WEXITSTATUS(status) == 42, 1817470b5afSSamuel Holland "dereference after fork+exec\n"); 1827470b5afSSamuel Holland } else { 1837470b5afSSamuel Holland /* Will call execve_child(). */ 1847470b5afSSamuel Holland execve("/proc/self/exe", (char *const []) { "", NULL }, NULL); 1857470b5afSSamuel Holland } 1867470b5afSSamuel Holland } 1877470b5afSSamuel Holland 188*498d5b14SCharlie Jenkins static bool pwrite_wrapper(int fd, void *buf, size_t count, const char *msg) 189*498d5b14SCharlie Jenkins { 190*498d5b14SCharlie Jenkins int ret = pwrite(fd, buf, count, 0); 191*498d5b14SCharlie Jenkins 192*498d5b14SCharlie Jenkins if (ret != count) { 193*498d5b14SCharlie Jenkins ksft_perror(msg); 194*498d5b14SCharlie Jenkins return false; 195*498d5b14SCharlie Jenkins } 196*498d5b14SCharlie Jenkins return true; 197*498d5b14SCharlie Jenkins } 198*498d5b14SCharlie Jenkins 1997470b5afSSamuel Holland static void test_tagged_addr_abi_sysctl(void) 2007470b5afSSamuel Holland { 201*498d5b14SCharlie Jenkins char *err_pwrite_msg = "failed to write to /proc/sys/abi/tagged_addr_disabled\n"; 2027470b5afSSamuel Holland char value; 2037470b5afSSamuel Holland int fd; 2047470b5afSSamuel Holland 2057470b5afSSamuel Holland ksft_print_msg("Testing tagged address ABI sysctl\n"); 2067470b5afSSamuel Holland 2077470b5afSSamuel Holland fd = open("/proc/sys/abi/tagged_addr_disabled", O_WRONLY); 2087470b5afSSamuel Holland if (fd < 0) { 2097470b5afSSamuel Holland ksft_test_result_skip("failed to open sysctl file\n"); 2107470b5afSSamuel Holland ksft_test_result_skip("failed to open sysctl file\n"); 2117470b5afSSamuel Holland return; 2127470b5afSSamuel Holland } 2137470b5afSSamuel Holland 2147470b5afSSamuel Holland value = '1'; 215*498d5b14SCharlie Jenkins if (!pwrite_wrapper(fd, &value, 1, "write '1'")) 216*498d5b14SCharlie Jenkins ksft_test_result_fail(err_pwrite_msg); 217*498d5b14SCharlie Jenkins else 2187470b5afSSamuel Holland ksft_test_result(set_tagged_addr_ctrl(min_pmlen, true) == -EINVAL, 2197470b5afSSamuel Holland "sysctl disabled\n"); 2207470b5afSSamuel Holland 2217470b5afSSamuel Holland value = '0'; 222*498d5b14SCharlie Jenkins if (!pwrite_wrapper(fd, &value, 1, "write '0'")) 223*498d5b14SCharlie Jenkins ksft_test_result_fail(err_pwrite_msg); 224*498d5b14SCharlie Jenkins else 2257470b5afSSamuel Holland ksft_test_result(set_tagged_addr_ctrl(min_pmlen, true) == 0, 2267470b5afSSamuel Holland "sysctl enabled\n"); 2277470b5afSSamuel Holland 2287470b5afSSamuel Holland set_tagged_addr_ctrl(0, false); 2297470b5afSSamuel Holland 2307470b5afSSamuel Holland close(fd); 2317470b5afSSamuel Holland } 2327470b5afSSamuel Holland 2337470b5afSSamuel Holland static void test_tagged_addr_abi_pmlen(int pmlen) 2347470b5afSSamuel Holland { 2357470b5afSSamuel Holland int i, *p, ret; 2367470b5afSSamuel Holland 2377470b5afSSamuel Holland i = ~pmlen; 2387470b5afSSamuel Holland 2397470b5afSSamuel Holland if (pmlen) { 2407470b5afSSamuel Holland p = (int *)((uintptr_t)&i | 1UL << (__riscv_xlen - pmlen)); 2417470b5afSSamuel Holland 2427470b5afSSamuel Holland ret = set_tagged_addr_ctrl(pmlen, false); 2437470b5afSSamuel Holland if (ret) 2447470b5afSSamuel Holland return ksft_test_result_error("PMLEN=%d ABI disabled setup (%d)\n", 2457470b5afSSamuel Holland pmlen, ret); 2467470b5afSSamuel Holland 2477470b5afSSamuel Holland ret = write(pipefd[1], p, sizeof(*p)); 2487470b5afSSamuel Holland if (ret >= 0 || errno != EFAULT) 2497470b5afSSamuel Holland return ksft_test_result_fail("PMLEN=%d ABI disabled write\n", pmlen); 2507470b5afSSamuel Holland 2517470b5afSSamuel Holland ret = read(dev_zero, p, sizeof(*p)); 2527470b5afSSamuel Holland if (ret >= 0 || errno != EFAULT) 2537470b5afSSamuel Holland return ksft_test_result_fail("PMLEN=%d ABI disabled read\n", pmlen); 2547470b5afSSamuel Holland 2557470b5afSSamuel Holland if (i != ~pmlen) 2567470b5afSSamuel Holland return ksft_test_result_fail("PMLEN=%d ABI disabled value\n", pmlen); 2577470b5afSSamuel Holland 2587470b5afSSamuel Holland ret = set_tagged_addr_ctrl(pmlen, true); 2597470b5afSSamuel Holland if (ret) 2607470b5afSSamuel Holland return ksft_test_result_error("PMLEN=%d ABI enabled setup (%d)\n", 2617470b5afSSamuel Holland pmlen, ret); 2627470b5afSSamuel Holland 2637470b5afSSamuel Holland ret = write(pipefd[1], p, sizeof(*p)); 2647470b5afSSamuel Holland if (ret != sizeof(*p)) 2657470b5afSSamuel Holland return ksft_test_result_fail("PMLEN=%d ABI enabled write\n", pmlen); 2667470b5afSSamuel Holland 2677470b5afSSamuel Holland ret = read(dev_zero, p, sizeof(*p)); 2687470b5afSSamuel Holland if (ret != sizeof(*p)) 2697470b5afSSamuel Holland return ksft_test_result_fail("PMLEN=%d ABI enabled read\n", pmlen); 2707470b5afSSamuel Holland 2717470b5afSSamuel Holland if (i) 2727470b5afSSamuel Holland return ksft_test_result_fail("PMLEN=%d ABI enabled value\n", pmlen); 2737470b5afSSamuel Holland 2747470b5afSSamuel Holland i = ~pmlen; 2757470b5afSSamuel Holland } else { 2767470b5afSSamuel Holland /* The tagged address ABI cannot be enabled when PMLEN == 0. */ 2777470b5afSSamuel Holland ret = set_tagged_addr_ctrl(pmlen, true); 2787470b5afSSamuel Holland if (ret != -EINVAL) 2797470b5afSSamuel Holland return ksft_test_result_error("PMLEN=%d ABI setup (%d)\n", 2807470b5afSSamuel Holland pmlen, ret); 2817470b5afSSamuel Holland } 2827470b5afSSamuel Holland 2837470b5afSSamuel Holland p = (int *)((uintptr_t)&i | 1UL << (__riscv_xlen - pmlen - 1)); 2847470b5afSSamuel Holland 2857470b5afSSamuel Holland ret = write(pipefd[1], p, sizeof(*p)); 2867470b5afSSamuel Holland if (ret >= 0 || errno != EFAULT) 2877470b5afSSamuel Holland return ksft_test_result_fail("PMLEN=%d invalid tag write (%d)\n", pmlen, errno); 2887470b5afSSamuel Holland 2897470b5afSSamuel Holland ret = read(dev_zero, p, sizeof(*p)); 2907470b5afSSamuel Holland if (ret >= 0 || errno != EFAULT) 2917470b5afSSamuel Holland return ksft_test_result_fail("PMLEN=%d invalid tag read\n", pmlen); 2927470b5afSSamuel Holland 2937470b5afSSamuel Holland if (i != ~pmlen) 2947470b5afSSamuel Holland return ksft_test_result_fail("PMLEN=%d invalid tag value\n", pmlen); 2957470b5afSSamuel Holland 2967470b5afSSamuel Holland ksft_test_result_pass("PMLEN=%d tagged address ABI\n", pmlen); 2977470b5afSSamuel Holland } 2987470b5afSSamuel Holland 2997470b5afSSamuel Holland static void test_tagged_addr_abi(void) 3007470b5afSSamuel Holland { 3017470b5afSSamuel Holland ksft_print_msg("Testing tagged address ABI\n"); 3027470b5afSSamuel Holland 3037470b5afSSamuel Holland test_tagged_addr_abi_pmlen(0); 3047470b5afSSamuel Holland test_tagged_addr_abi_pmlen(min_pmlen); 3057470b5afSSamuel Holland test_tagged_addr_abi_pmlen(max_pmlen); 3067470b5afSSamuel Holland } 3077470b5afSSamuel Holland 3087470b5afSSamuel Holland static struct test_info { 3097470b5afSSamuel Holland unsigned int nr_tests; 3107470b5afSSamuel Holland void (*test_fn)(void); 3117470b5afSSamuel Holland } tests[] = { 3127470b5afSSamuel Holland { .nr_tests = 17 * 3, test_pmlen }, 3137470b5afSSamuel Holland { .nr_tests = 3, test_dereference }, 3147470b5afSSamuel Holland { .nr_tests = 2, test_fork_exec }, 3157470b5afSSamuel Holland { .nr_tests = 2, test_tagged_addr_abi_sysctl }, 3167470b5afSSamuel Holland { .nr_tests = 3, test_tagged_addr_abi }, 3177470b5afSSamuel Holland }; 3187470b5afSSamuel Holland 3197470b5afSSamuel Holland int main(int argc, char **argv) 3207470b5afSSamuel Holland { 3217470b5afSSamuel Holland unsigned int plan = 0; 3227470b5afSSamuel Holland int ret; 3237470b5afSSamuel Holland 3247470b5afSSamuel Holland /* Check if this is the child process after execve(). */ 3257470b5afSSamuel Holland if (!argv[0][0]) 3267470b5afSSamuel Holland return execve_child(); 3277470b5afSSamuel Holland 3287470b5afSSamuel Holland dev_zero = open("/dev/zero", O_RDWR); 3297470b5afSSamuel Holland if (dev_zero < 0) 3307470b5afSSamuel Holland return 1; 3317470b5afSSamuel Holland 3327470b5afSSamuel Holland /* Write to a pipe so the kernel must dereference the buffer pointer. */ 3337470b5afSSamuel Holland ret = pipe(pipefd); 3347470b5afSSamuel Holland if (ret) 3357470b5afSSamuel Holland return 1; 3367470b5afSSamuel Holland 3377470b5afSSamuel Holland ksft_print_header(); 3387470b5afSSamuel Holland 3397470b5afSSamuel Holland for (int i = 0; i < ARRAY_SIZE(tests); i++) 3407470b5afSSamuel Holland plan += tests[i].nr_tests; 3417470b5afSSamuel Holland 3427470b5afSSamuel Holland ksft_set_plan(plan); 3437470b5afSSamuel Holland 3447470b5afSSamuel Holland for (int i = 0; i < ARRAY_SIZE(tests); i++) 3457470b5afSSamuel Holland tests[i].test_fn(); 3467470b5afSSamuel Holland 3477470b5afSSamuel Holland ksft_finished(); 3487470b5afSSamuel Holland } 349