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

Configure Feed

Select the types of activity you want to include in your feed.

at v6.12-rc2 233 lines 5.1 kB view raw
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 21static int require_nphie(void) 22{ 23 SKIP_IF_MSG(!dexcr_exists(), "DEXCR not supported"); 24 25 pr_set_dexcr(PR_PPC_DEXCR_NPHIE, PR_PPC_DEXCR_CTRL_SET | PR_PPC_DEXCR_CTRL_SET_ONEXEC); 26 27 if (get_dexcr(EFFECTIVE) & DEXCR_PR_NPHIE) 28 return 0; 29 30 SKIP_IF_MSG(!(get_dexcr(EFFECTIVE) & DEXCR_PR_NPHIE), 31 "Failed to enable DEXCR[NPHIE]"); 32 33 return 0; 34} 35 36static jmp_buf hashchk_detected_buf; 37static const char *hashchk_failure_msg; 38 39static void hashchk_handler(int signum, siginfo_t *info, void *context) 40{ 41 if (signum != SIGILL) 42 hashchk_failure_msg = "wrong signal received"; 43 else if (info->si_code != ILL_ILLOPN) 44 hashchk_failure_msg = "wrong signal code received"; 45 46 longjmp(hashchk_detected_buf, 0); 47} 48 49/* 50 * Check that hashchk triggers when DEXCR[NPHIE] is enabled 51 * and is detected as such by the kernel exception handler 52 */ 53static int hashchk_detected_test(void) 54{ 55 struct sigaction old; 56 int err; 57 58 err = require_nphie(); 59 if (err) 60 return err; 61 62 old = push_signal_handler(SIGILL, hashchk_handler); 63 if (setjmp(hashchk_detected_buf)) 64 goto out; 65 66 hashchk_failure_msg = NULL; 67 do_bad_hashchk(); 68 hashchk_failure_msg = "hashchk failed to trigger"; 69 70out: 71 pop_signal_handler(SIGILL, old); 72 FAIL_IF_MSG(hashchk_failure_msg, hashchk_failure_msg); 73 return 0; 74} 75 76#define HASH_COUNT 8 77 78static unsigned long hash_values[HASH_COUNT + 1]; 79 80static void fill_hash_values(void) 81{ 82 for (unsigned long i = 0; i < HASH_COUNT; i++) 83 hashst(i, &hash_values[i]); 84 85 /* Used to ensure the checks uses the same addresses as the hashes */ 86 hash_values[HASH_COUNT] = (unsigned long)&hash_values; 87} 88 89static unsigned int count_hash_values_matches(void) 90{ 91 unsigned long matches = 0; 92 93 for (unsigned long i = 0; i < HASH_COUNT; i++) { 94 unsigned long orig_hash = hash_values[i]; 95 hash_values[i] = 0; 96 97 hashst(i, &hash_values[i]); 98 99 if (hash_values[i] == orig_hash) 100 matches++; 101 } 102 103 return matches; 104} 105 106static int hashchk_exec_child(void) 107{ 108 ssize_t count; 109 110 fill_hash_values(); 111 112 count = write(STDOUT_FILENO, hash_values, sizeof(hash_values)); 113 return count == sizeof(hash_values) ? 0 : EOVERFLOW; 114} 115 116static char *hashchk_exec_child_args[] = { "hashchk_exec_child", NULL }; 117 118/* 119 * Check that new programs get different keys so a malicious process 120 * can't recreate a victim's hash values. 121 */ 122static int hashchk_exec_random_key_test(void) 123{ 124 pid_t pid; 125 int err; 126 int pipefd[2]; 127 128 err = require_nphie(); 129 if (err) 130 return err; 131 132 FAIL_IF_MSG(pipe(pipefd), "failed to create pipe"); 133 134 pid = fork(); 135 if (pid == 0) { 136 if (dup2(pipefd[1], STDOUT_FILENO) == -1) 137 _exit(errno); 138 139 execve("/proc/self/exe", hashchk_exec_child_args, NULL); 140 _exit(errno); 141 } 142 143 await_child_success(pid); 144 FAIL_IF_MSG(read(pipefd[0], hash_values, sizeof(hash_values)) != sizeof(hash_values), 145 "missing expected child output"); 146 147 /* Verify the child used the same hash_values address */ 148 FAIL_IF_EXIT_MSG(hash_values[HASH_COUNT] != (unsigned long)&hash_values, 149 "bad address check"); 150 151 /* If all hashes are the same it means (most likely) same key */ 152 FAIL_IF_MSG(count_hash_values_matches() == HASH_COUNT, "shared key detected"); 153 154 return 0; 155} 156 157/* 158 * Check that forks share the same key so that existing hash values 159 * remain valid. 160 */ 161static int hashchk_fork_share_key_test(void) 162{ 163 pid_t pid; 164 int err; 165 166 err = require_nphie(); 167 if (err) 168 return err; 169 170 fill_hash_values(); 171 172 pid = fork(); 173 if (pid == 0) { 174 if (count_hash_values_matches() != HASH_COUNT) 175 _exit(1); 176 _exit(0); 177 } 178 179 await_child_success(pid); 180 return 0; 181} 182 183#define STACK_SIZE (1024 * 1024) 184 185static int hashchk_clone_child_fn(void *args) 186{ 187 fill_hash_values(); 188 return 0; 189} 190 191/* 192 * Check that threads share the same key so that existing hash values 193 * remain valid. 194 */ 195static int hashchk_clone_share_key_test(void) 196{ 197 void *child_stack; 198 pid_t pid; 199 int err; 200 201 err = require_nphie(); 202 if (err) 203 return err; 204 205 child_stack = mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE, 206 MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); 207 208 FAIL_IF_MSG(child_stack == MAP_FAILED, "failed to map child stack"); 209 210 pid = clone(hashchk_clone_child_fn, child_stack + STACK_SIZE, 211 CLONE_VM | SIGCHLD, NULL); 212 213 await_child_success(pid); 214 FAIL_IF_MSG(count_hash_values_matches() != HASH_COUNT, 215 "different key detected"); 216 217 return 0; 218} 219 220int main(int argc, char *argv[]) 221{ 222 int err = 0; 223 224 if (argc >= 1 && !strcmp(argv[0], hashchk_exec_child_args[0])) 225 return hashchk_exec_child(); 226 227 err |= test_harness(hashchk_detected_test, "hashchk_detected"); 228 err |= test_harness(hashchk_exec_random_key_test, "hashchk_exec_random_key"); 229 err |= test_harness(hashchk_fork_share_key_test, "hashchk_fork_share_key"); 230 err |= test_harness(hashchk_clone_share_key_test, "hashchk_clone_share_key"); 231 232 return err; 233}