File Index
Files and directories
Sign in
FILE

hashchk_test.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/powerpc/dexcr/hashchk_test.c 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
 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}
~/ Read-only file preview.