Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/powerpc/dexcr: Add hashst/hashchk test

Test the kernel DEXCR[NPHIE] interface and hashchk exception handling.

Introduces with it a DEXCR utils library for common DEXCR operations.

Volatile is used to prevent the compiler optimising away the signal
tests.

Signed-off-by: Benjamin Gray <bgray@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://msgid.link/20230616034846.311705-11-bgray@linux.ibm.com

authored by

Benjamin Gray and committed by
Michael Ellerman
bdb07f35 b9125c9a

+449
+1
tools/testing/selftests/powerpc/Makefile
··· 17 17 benchmarks \ 18 18 cache_shape \ 19 19 copyloops \ 20 + dexcr \ 20 21 dscr \ 21 22 mm \ 22 23 nx-gzip \
+1
tools/testing/selftests/powerpc/dexcr/.gitignore
··· 1 + hashchk_test
+7
tools/testing/selftests/powerpc/dexcr/Makefile
··· 1 + TEST_GEN_PROGS := hashchk_test 2 + 3 + include ../../lib.mk 4 + 5 + $(OUTPUT)/hashchk_test: CFLAGS += -fno-pie $(call cc-option,-mno-rop-protect) 6 + 7 + $(TEST_GEN_PROGS): ../harness.c ../utils.c ./dexcr.c
+132
tools/testing/selftests/powerpc/dexcr/dexcr.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + 3 + #include <errno.h> 4 + #include <setjmp.h> 5 + #include <signal.h> 6 + #include <sys/types.h> 7 + #include <sys/wait.h> 8 + 9 + #include "dexcr.h" 10 + #include "reg.h" 11 + #include "utils.h" 12 + 13 + static jmp_buf generic_signal_jump_buf; 14 + 15 + static void generic_signal_handler(int signum, siginfo_t *info, void *context) 16 + { 17 + longjmp(generic_signal_jump_buf, 0); 18 + } 19 + 20 + bool dexcr_exists(void) 21 + { 22 + struct sigaction old; 23 + volatile bool exists; 24 + 25 + old = push_signal_handler(SIGILL, generic_signal_handler); 26 + if (setjmp(generic_signal_jump_buf)) 27 + goto out; 28 + 29 + /* 30 + * If the SPR is not recognised by the hardware it triggers 31 + * a hypervisor emulation interrupt. If the kernel does not 32 + * recognise/try to emulate it, we receive a SIGILL signal. 33 + * 34 + * If we do not receive a signal, assume we have the SPR or the 35 + * kernel is trying to emulate it correctly. 36 + */ 37 + exists = false; 38 + mfspr(SPRN_DEXCR_RO); 39 + exists = true; 40 + 41 + out: 42 + pop_signal_handler(SIGILL, old); 43 + return exists; 44 + } 45 + 46 + /* 47 + * Just test if a bad hashchk triggers a signal, without checking 48 + * for support or if the NPHIE aspect is enabled. 49 + */ 50 + bool hashchk_triggers(void) 51 + { 52 + struct sigaction old; 53 + volatile bool triggers; 54 + 55 + old = push_signal_handler(SIGILL, generic_signal_handler); 56 + if (setjmp(generic_signal_jump_buf)) 57 + goto out; 58 + 59 + triggers = true; 60 + do_bad_hashchk(); 61 + triggers = false; 62 + 63 + out: 64 + pop_signal_handler(SIGILL, old); 65 + return triggers; 66 + } 67 + 68 + unsigned int get_dexcr(enum dexcr_source source) 69 + { 70 + switch (source) { 71 + case DEXCR: 72 + return mfspr(SPRN_DEXCR_RO); 73 + case HDEXCR: 74 + return mfspr(SPRN_HDEXCR_RO); 75 + case EFFECTIVE: 76 + return mfspr(SPRN_DEXCR_RO) | mfspr(SPRN_HDEXCR_RO); 77 + default: 78 + FAIL_IF_EXIT_MSG(true, "bad enum dexcr_source"); 79 + } 80 + } 81 + 82 + void await_child_success(pid_t pid) 83 + { 84 + int wstatus; 85 + 86 + FAIL_IF_EXIT_MSG(pid == -1, "fork failed"); 87 + FAIL_IF_EXIT_MSG(waitpid(pid, &wstatus, 0) == -1, "wait failed"); 88 + FAIL_IF_EXIT_MSG(!WIFEXITED(wstatus), "child did not exit cleanly"); 89 + FAIL_IF_EXIT_MSG(WEXITSTATUS(wstatus) != 0, "child exit error"); 90 + } 91 + 92 + /* 93 + * Perform a hashst instruction. The following components determine the result 94 + * 95 + * 1. The LR value (any register technically) 96 + * 2. The SP value (also any register, but it must be a valid address) 97 + * 3. A secret key managed by the kernel 98 + * 99 + * The result is stored to the address held in SP. 100 + */ 101 + void hashst(unsigned long lr, void *sp) 102 + { 103 + asm volatile ("addi 31, %0, 0;" /* set r31 (pretend LR) to lr */ 104 + "addi 30, %1, 8;" /* set r30 (pretend SP) to sp + 8 */ 105 + PPC_RAW_HASHST(31, -8, 30) /* compute hash into stack location */ 106 + : : "r" (lr), "r" (sp) : "r31", "r30", "memory"); 107 + } 108 + 109 + /* 110 + * Perform a hashchk instruction. A hash is computed as per hashst(), 111 + * however the result is not stored to memory. Instead the existing 112 + * value is read and compared against the computed hash. 113 + * 114 + * If they match, execution continues. 115 + * If they differ, an interrupt triggers. 116 + */ 117 + void hashchk(unsigned long lr, void *sp) 118 + { 119 + asm volatile ("addi 31, %0, 0;" /* set r31 (pretend LR) to lr */ 120 + "addi 30, %1, 8;" /* set r30 (pretend SP) to sp + 8 */ 121 + PPC_RAW_HASHCHK(31, -8, 30) /* check hash at stack location */ 122 + : : "r" (lr), "r" (sp) : "r31", "r30", "memory"); 123 + } 124 + 125 + void do_bad_hashchk(void) 126 + { 127 + unsigned long hash = 0; 128 + 129 + hashst(0, &hash); 130 + hash += 1; 131 + hashchk(0, &hash); 132 + }
+49
tools/testing/selftests/powerpc/dexcr/dexcr.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + /* 3 + * POWER Dynamic Execution Control Facility (DEXCR) 4 + * 5 + * This header file contains helper functions and macros 6 + * required for all the DEXCR related test cases. 7 + */ 8 + #ifndef _SELFTESTS_POWERPC_DEXCR_DEXCR_H 9 + #define _SELFTESTS_POWERPC_DEXCR_DEXCR_H 10 + 11 + #include <stdbool.h> 12 + #include <sys/types.h> 13 + 14 + #include "reg.h" 15 + 16 + #define DEXCR_PR_BIT(aspect) __MASK(63 - (32 + (aspect))) 17 + #define DEXCR_PR_SBHE DEXCR_PR_BIT(0) 18 + #define DEXCR_PR_IBRTPD DEXCR_PR_BIT(3) 19 + #define DEXCR_PR_SRAPD DEXCR_PR_BIT(4) 20 + #define DEXCR_PR_NPHIE DEXCR_PR_BIT(5) 21 + 22 + #define PPC_RAW_HASH_ARGS(b, i, a) \ 23 + ((((i) >> 3) & 0x1F) << 21 | (a) << 16 | (b) << 11 | (((i) >> 8) & 0x1)) 24 + #define PPC_RAW_HASHST(b, i, a) \ 25 + str(.long (0x7C0005A4 | PPC_RAW_HASH_ARGS(b, i, a));) 26 + #define PPC_RAW_HASHCHK(b, i, a) \ 27 + str(.long (0x7C0005E4 | PPC_RAW_HASH_ARGS(b, i, a));) 28 + 29 + bool dexcr_exists(void); 30 + 31 + bool hashchk_triggers(void); 32 + 33 + enum dexcr_source { 34 + DEXCR, /* Userspace DEXCR value */ 35 + HDEXCR, /* Hypervisor enforced DEXCR value */ 36 + EFFECTIVE, /* Bitwise OR of UDEXCR and ENFORCED DEXCR bits */ 37 + }; 38 + 39 + unsigned int get_dexcr(enum dexcr_source source); 40 + 41 + void await_child_success(pid_t pid); 42 + 43 + void hashst(unsigned long lr, void *sp); 44 + 45 + void hashchk(unsigned long lr, void *sp); 46 + 47 + void do_bad_hashchk(void); 48 + 49 + #endif /* _SELFTESTS_POWERPC_DEXCR_DEXCR_H */
+227
tools/testing/selftests/powerpc/dexcr/hashchk_test.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + 3 + #define _GNU_SOURCE 4 + 5 + #include <errno.h> 6 + #include <fcntl.h> 7 + #include <limits.h> 8 + #include <sched.h> 9 + #include <setjmp.h> 10 + #include <signal.h> 11 + #include <stdio.h> 12 + #include <stdlib.h> 13 + #include <string.h> 14 + #include <sys/mman.h> 15 + #include <sys/prctl.h> 16 + #include <unistd.h> 17 + 18 + #include "dexcr.h" 19 + #include "utils.h" 20 + 21 + static int require_nphie(void) 22 + { 23 + SKIP_IF_MSG(!dexcr_exists(), "DEXCR not supported"); 24 + SKIP_IF_MSG(!(get_dexcr(EFFECTIVE) & DEXCR_PR_NPHIE), 25 + "DEXCR[NPHIE] not enabled"); 26 + 27 + return 0; 28 + } 29 + 30 + static jmp_buf hashchk_detected_buf; 31 + static const char *hashchk_failure_msg; 32 + 33 + static void hashchk_handler(int signum, siginfo_t *info, void *context) 34 + { 35 + if (signum != SIGILL) 36 + hashchk_failure_msg = "wrong signal received"; 37 + else if (info->si_code != ILL_ILLOPN) 38 + hashchk_failure_msg = "wrong signal code received"; 39 + 40 + longjmp(hashchk_detected_buf, 0); 41 + } 42 + 43 + /* 44 + * Check that hashchk triggers when DEXCR[NPHIE] is enabled 45 + * and is detected as such by the kernel exception handler 46 + */ 47 + static int hashchk_detected_test(void) 48 + { 49 + struct sigaction old; 50 + int err; 51 + 52 + err = require_nphie(); 53 + if (err) 54 + return err; 55 + 56 + old = push_signal_handler(SIGILL, hashchk_handler); 57 + if (setjmp(hashchk_detected_buf)) 58 + goto out; 59 + 60 + hashchk_failure_msg = NULL; 61 + do_bad_hashchk(); 62 + hashchk_failure_msg = "hashchk failed to trigger"; 63 + 64 + out: 65 + pop_signal_handler(SIGILL, old); 66 + FAIL_IF_MSG(hashchk_failure_msg, hashchk_failure_msg); 67 + return 0; 68 + } 69 + 70 + #define HASH_COUNT 8 71 + 72 + static unsigned long hash_values[HASH_COUNT + 1]; 73 + 74 + static void fill_hash_values(void) 75 + { 76 + for (unsigned long i = 0; i < HASH_COUNT; i++) 77 + hashst(i, &hash_values[i]); 78 + 79 + /* Used to ensure the checks uses the same addresses as the hashes */ 80 + hash_values[HASH_COUNT] = (unsigned long)&hash_values; 81 + } 82 + 83 + static unsigned int count_hash_values_matches(void) 84 + { 85 + unsigned long matches = 0; 86 + 87 + for (unsigned long i = 0; i < HASH_COUNT; i++) { 88 + unsigned long orig_hash = hash_values[i]; 89 + hash_values[i] = 0; 90 + 91 + hashst(i, &hash_values[i]); 92 + 93 + if (hash_values[i] == orig_hash) 94 + matches++; 95 + } 96 + 97 + return matches; 98 + } 99 + 100 + static int hashchk_exec_child(void) 101 + { 102 + ssize_t count; 103 + 104 + fill_hash_values(); 105 + 106 + count = write(STDOUT_FILENO, hash_values, sizeof(hash_values)); 107 + return count == sizeof(hash_values) ? 0 : EOVERFLOW; 108 + } 109 + 110 + static char *hashchk_exec_child_args[] = { "hashchk_exec_child", NULL }; 111 + 112 + /* 113 + * Check that new programs get different keys so a malicious process 114 + * can't recreate a victim's hash values. 115 + */ 116 + static int hashchk_exec_random_key_test(void) 117 + { 118 + pid_t pid; 119 + int err; 120 + int pipefd[2]; 121 + 122 + err = require_nphie(); 123 + if (err) 124 + return err; 125 + 126 + FAIL_IF_MSG(pipe(pipefd), "failed to create pipe"); 127 + 128 + pid = fork(); 129 + if (pid == 0) { 130 + if (dup2(pipefd[1], STDOUT_FILENO) == -1) 131 + _exit(errno); 132 + 133 + execve("/proc/self/exe", hashchk_exec_child_args, NULL); 134 + _exit(errno); 135 + } 136 + 137 + await_child_success(pid); 138 + FAIL_IF_MSG(read(pipefd[0], hash_values, sizeof(hash_values)) != sizeof(hash_values), 139 + "missing expected child output"); 140 + 141 + /* Verify the child used the same hash_values address */ 142 + FAIL_IF_EXIT_MSG(hash_values[HASH_COUNT] != (unsigned long)&hash_values, 143 + "bad address check"); 144 + 145 + /* If all hashes are the same it means (most likely) same key */ 146 + FAIL_IF_MSG(count_hash_values_matches() == HASH_COUNT, "shared key detected"); 147 + 148 + return 0; 149 + } 150 + 151 + /* 152 + * Check that forks share the same key so that existing hash values 153 + * remain valid. 154 + */ 155 + static int hashchk_fork_share_key_test(void) 156 + { 157 + pid_t pid; 158 + int err; 159 + 160 + err = require_nphie(); 161 + if (err) 162 + return err; 163 + 164 + fill_hash_values(); 165 + 166 + pid = fork(); 167 + if (pid == 0) { 168 + if (count_hash_values_matches() != HASH_COUNT) 169 + _exit(1); 170 + _exit(0); 171 + } 172 + 173 + await_child_success(pid); 174 + return 0; 175 + } 176 + 177 + #define STACK_SIZE (1024 * 1024) 178 + 179 + static int hashchk_clone_child_fn(void *args) 180 + { 181 + fill_hash_values(); 182 + return 0; 183 + } 184 + 185 + /* 186 + * Check that threads share the same key so that existing hash values 187 + * remain valid. 188 + */ 189 + static int hashchk_clone_share_key_test(void) 190 + { 191 + void *child_stack; 192 + pid_t pid; 193 + int err; 194 + 195 + err = require_nphie(); 196 + if (err) 197 + return err; 198 + 199 + child_stack = mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE, 200 + MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); 201 + 202 + FAIL_IF_MSG(child_stack == MAP_FAILED, "failed to map child stack"); 203 + 204 + pid = clone(hashchk_clone_child_fn, child_stack + STACK_SIZE, 205 + CLONE_VM | SIGCHLD, NULL); 206 + 207 + await_child_success(pid); 208 + FAIL_IF_MSG(count_hash_values_matches() != HASH_COUNT, 209 + "different key detected"); 210 + 211 + return 0; 212 + } 213 + 214 + int main(int argc, char *argv[]) 215 + { 216 + int err = 0; 217 + 218 + if (argc >= 1 && !strcmp(argv[0], hashchk_exec_child_args[0])) 219 + return hashchk_exec_child(); 220 + 221 + err |= test_harness(hashchk_detected_test, "hashchk_detected"); 222 + err |= test_harness(hashchk_exec_random_key_test, "hashchk_exec_random_key"); 223 + err |= test_harness(hashchk_fork_share_key_test, "hashchk_fork_share_key"); 224 + err |= test_harness(hashchk_clone_share_key_test, "hashchk_clone_share_key"); 225 + 226 + return err; 227 + }
+4
tools/testing/selftests/powerpc/include/reg.h
··· 19 19 #define mb() asm volatile("sync" : : : "memory"); 20 20 #define barrier() asm volatile("" : : : "memory"); 21 21 22 + #define SPRN_HDEXCR_RO 455 /* Userspace readonly view of SPRN_HDEXCR (471) */ 23 + 22 24 #define SPRN_MMCR2 769 23 25 #define SPRN_MMCRA 770 24 26 #define SPRN_MMCR0 779 ··· 48 46 #define SPRN_SIAR 780 49 47 #define SPRN_SDAR 781 50 48 #define SPRN_SIER 768 49 + 50 + #define SPRN_DEXCR_RO 812 /* Userspace readonly view of SPRN_DEXCR (828) */ 51 51 52 52 #define SPRN_TEXASR 0x82 /* Transaction Exception and Status Register */ 53 53 #define SPRN_TFIAR 0x81 /* Transaction Failure Inst Addr */
+4
tools/testing/selftests/powerpc/include/utils.h
··· 11 11 #include <stdint.h> 12 12 #include <stdio.h> 13 13 #include <stdbool.h> 14 + #include <sys/signal.h> 14 15 #include <linux/auxvec.h> 15 16 #include <linux/perf_event.h> 16 17 #include <asm/cputable.h> ··· 111 110 112 111 bool is_ppc64le(void); 113 112 int using_hash_mmu(bool *using_hash); 113 + 114 + struct sigaction push_signal_handler(int sig, void (*fn)(int, siginfo_t *, void *)); 115 + struct sigaction pop_signal_handler(int sig, struct sigaction old_handler); 114 116 115 117 /* Yes, this is evil */ 116 118 #define FAIL_IF(x) \
+24
tools/testing/selftests/powerpc/utils.c
··· 618 618 fclose(f); 619 619 return rc; 620 620 } 621 + 622 + struct sigaction push_signal_handler(int sig, void (*fn)(int, siginfo_t *, void *)) 623 + { 624 + struct sigaction sa; 625 + struct sigaction old_handler; 626 + 627 + sa.sa_sigaction = fn; 628 + sigemptyset(&sa.sa_mask); 629 + sa.sa_flags = SA_SIGINFO; 630 + FAIL_IF_EXIT_MSG(sigaction(sig, &sa, &old_handler), 631 + "failed to push signal handler"); 632 + 633 + return old_handler; 634 + } 635 + 636 + struct sigaction pop_signal_handler(int sig, struct sigaction old_handler) 637 + { 638 + struct sigaction popped; 639 + 640 + FAIL_IF_EXIT_MSG(sigaction(sig, &old_handler, &popped), 641 + "failed to pop signal handler"); 642 + 643 + return popped; 644 + }