brintos

brintos / linux-shallow public Read only

0
0
Text · 4.9 KiB · 645224b Raw
234 lines · c
1// SPDX-License-Identifier: GPL-2.0+2 3#define _GNU_SOURCE4 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 enabled51 * and is detected as such by the kernel exception handler52 */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 877 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 process120 * 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 values159 * 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 values193 * 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}234