162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#define _GNU_SOURCE 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <errno.h> 662306a36Sopenharmony_ci#include <fcntl.h> 762306a36Sopenharmony_ci#include <limits.h> 862306a36Sopenharmony_ci#include <sched.h> 962306a36Sopenharmony_ci#include <setjmp.h> 1062306a36Sopenharmony_ci#include <signal.h> 1162306a36Sopenharmony_ci#include <stdio.h> 1262306a36Sopenharmony_ci#include <stdlib.h> 1362306a36Sopenharmony_ci#include <string.h> 1462306a36Sopenharmony_ci#include <sys/mman.h> 1562306a36Sopenharmony_ci#include <sys/prctl.h> 1662306a36Sopenharmony_ci#include <unistd.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "dexcr.h" 1962306a36Sopenharmony_ci#include "utils.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic int require_nphie(void) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci SKIP_IF_MSG(!dexcr_exists(), "DEXCR not supported"); 2462306a36Sopenharmony_ci SKIP_IF_MSG(!(get_dexcr(EFFECTIVE) & DEXCR_PR_NPHIE), 2562306a36Sopenharmony_ci "DEXCR[NPHIE] not enabled"); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci return 0; 2862306a36Sopenharmony_ci} 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic jmp_buf hashchk_detected_buf; 3162306a36Sopenharmony_cistatic const char *hashchk_failure_msg; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic void hashchk_handler(int signum, siginfo_t *info, void *context) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci if (signum != SIGILL) 3662306a36Sopenharmony_ci hashchk_failure_msg = "wrong signal received"; 3762306a36Sopenharmony_ci else if (info->si_code != ILL_ILLOPN) 3862306a36Sopenharmony_ci hashchk_failure_msg = "wrong signal code received"; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci longjmp(hashchk_detected_buf, 0); 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* 4462306a36Sopenharmony_ci * Check that hashchk triggers when DEXCR[NPHIE] is enabled 4562306a36Sopenharmony_ci * and is detected as such by the kernel exception handler 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_cistatic int hashchk_detected_test(void) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct sigaction old; 5062306a36Sopenharmony_ci int err; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci err = require_nphie(); 5362306a36Sopenharmony_ci if (err) 5462306a36Sopenharmony_ci return err; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci old = push_signal_handler(SIGILL, hashchk_handler); 5762306a36Sopenharmony_ci if (setjmp(hashchk_detected_buf)) 5862306a36Sopenharmony_ci goto out; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci hashchk_failure_msg = NULL; 6162306a36Sopenharmony_ci do_bad_hashchk(); 6262306a36Sopenharmony_ci hashchk_failure_msg = "hashchk failed to trigger"; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ciout: 6562306a36Sopenharmony_ci pop_signal_handler(SIGILL, old); 6662306a36Sopenharmony_ci FAIL_IF_MSG(hashchk_failure_msg, hashchk_failure_msg); 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#define HASH_COUNT 8 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic unsigned long hash_values[HASH_COUNT + 1]; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic void fill_hash_values(void) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci for (unsigned long i = 0; i < HASH_COUNT; i++) 7762306a36Sopenharmony_ci hashst(i, &hash_values[i]); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* Used to ensure the checks uses the same addresses as the hashes */ 8062306a36Sopenharmony_ci hash_values[HASH_COUNT] = (unsigned long)&hash_values; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic unsigned int count_hash_values_matches(void) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci unsigned long matches = 0; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci for (unsigned long i = 0; i < HASH_COUNT; i++) { 8862306a36Sopenharmony_ci unsigned long orig_hash = hash_values[i]; 8962306a36Sopenharmony_ci hash_values[i] = 0; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci hashst(i, &hash_values[i]); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (hash_values[i] == orig_hash) 9462306a36Sopenharmony_ci matches++; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci return matches; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic int hashchk_exec_child(void) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci ssize_t count; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci fill_hash_values(); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci count = write(STDOUT_FILENO, hash_values, sizeof(hash_values)); 10762306a36Sopenharmony_ci return count == sizeof(hash_values) ? 0 : EOVERFLOW; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic char *hashchk_exec_child_args[] = { "hashchk_exec_child", NULL }; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/* 11362306a36Sopenharmony_ci * Check that new programs get different keys so a malicious process 11462306a36Sopenharmony_ci * can't recreate a victim's hash values. 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_cistatic int hashchk_exec_random_key_test(void) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci pid_t pid; 11962306a36Sopenharmony_ci int err; 12062306a36Sopenharmony_ci int pipefd[2]; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci err = require_nphie(); 12362306a36Sopenharmony_ci if (err) 12462306a36Sopenharmony_ci return err; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci FAIL_IF_MSG(pipe(pipefd), "failed to create pipe"); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci pid = fork(); 12962306a36Sopenharmony_ci if (pid == 0) { 13062306a36Sopenharmony_ci if (dup2(pipefd[1], STDOUT_FILENO) == -1) 13162306a36Sopenharmony_ci _exit(errno); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci execve("/proc/self/exe", hashchk_exec_child_args, NULL); 13462306a36Sopenharmony_ci _exit(errno); 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci await_child_success(pid); 13862306a36Sopenharmony_ci FAIL_IF_MSG(read(pipefd[0], hash_values, sizeof(hash_values)) != sizeof(hash_values), 13962306a36Sopenharmony_ci "missing expected child output"); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* Verify the child used the same hash_values address */ 14262306a36Sopenharmony_ci FAIL_IF_EXIT_MSG(hash_values[HASH_COUNT] != (unsigned long)&hash_values, 14362306a36Sopenharmony_ci "bad address check"); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* If all hashes are the same it means (most likely) same key */ 14662306a36Sopenharmony_ci FAIL_IF_MSG(count_hash_values_matches() == HASH_COUNT, "shared key detected"); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/* 15262306a36Sopenharmony_ci * Check that forks share the same key so that existing hash values 15362306a36Sopenharmony_ci * remain valid. 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_cistatic int hashchk_fork_share_key_test(void) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci pid_t pid; 15862306a36Sopenharmony_ci int err; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci err = require_nphie(); 16162306a36Sopenharmony_ci if (err) 16262306a36Sopenharmony_ci return err; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci fill_hash_values(); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci pid = fork(); 16762306a36Sopenharmony_ci if (pid == 0) { 16862306a36Sopenharmony_ci if (count_hash_values_matches() != HASH_COUNT) 16962306a36Sopenharmony_ci _exit(1); 17062306a36Sopenharmony_ci _exit(0); 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci await_child_success(pid); 17462306a36Sopenharmony_ci return 0; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci#define STACK_SIZE (1024 * 1024) 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic int hashchk_clone_child_fn(void *args) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci fill_hash_values(); 18262306a36Sopenharmony_ci return 0; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/* 18662306a36Sopenharmony_ci * Check that threads share the same key so that existing hash values 18762306a36Sopenharmony_ci * remain valid. 18862306a36Sopenharmony_ci */ 18962306a36Sopenharmony_cistatic int hashchk_clone_share_key_test(void) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci void *child_stack; 19262306a36Sopenharmony_ci pid_t pid; 19362306a36Sopenharmony_ci int err; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci err = require_nphie(); 19662306a36Sopenharmony_ci if (err) 19762306a36Sopenharmony_ci return err; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci child_stack = mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE, 20062306a36Sopenharmony_ci MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci FAIL_IF_MSG(child_stack == MAP_FAILED, "failed to map child stack"); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci pid = clone(hashchk_clone_child_fn, child_stack + STACK_SIZE, 20562306a36Sopenharmony_ci CLONE_VM | SIGCHLD, NULL); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci await_child_success(pid); 20862306a36Sopenharmony_ci FAIL_IF_MSG(count_hash_values_matches() != HASH_COUNT, 20962306a36Sopenharmony_ci "different key detected"); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci return 0; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ciint main(int argc, char *argv[]) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci int err = 0; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (argc >= 1 && !strcmp(argv[0], hashchk_exec_child_args[0])) 21962306a36Sopenharmony_ci return hashchk_exec_child(); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci err |= test_harness(hashchk_detected_test, "hashchk_detected"); 22262306a36Sopenharmony_ci err |= test_harness(hashchk_exec_random_key_test, "hashchk_exec_random_key"); 22362306a36Sopenharmony_ci err |= test_harness(hashchk_fork_share_key_test, "hashchk_fork_share_key"); 22462306a36Sopenharmony_ci err |= test_harness(hashchk_clone_share_key_test, "hashchk_clone_share_key"); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci return err; 22762306a36Sopenharmony_ci} 228