xref: /linux/tools/testing/selftests/x86/xstate.c (revision dd8845db6d05e81532110c271d5f1cef3975916a)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #define _GNU_SOURCE
4 
5 #include <elf.h>
6 #include <pthread.h>
7 #include <stdbool.h>
8 
9 #include <asm/prctl.h>
10 #include <sys/ptrace.h>
11 #include <sys/syscall.h>
12 #include <sys/uio.h>
13 #include <sys/wait.h>
14 
15 #include "helpers.h"
16 #include "xstate.h"
17 
18 /*
19  * The userspace xstate test suite is designed to be generic and operates
20  * with randomized xstate data. However, some states require special handling:
21  *
22  * - PKRU and XTILECFG need specific adjustments, such as modifying
23  *   randomization behavior or using fixed values.
24  * - But, PKRU already has a dedicated test suite in /tools/selftests/mm.
25  * - Legacy states (FP and SSE) are excluded, as they are not considered
26  *   part of extended states (xstates) and their usage is already deeply
27  *   integrated into user-space libraries.
28  */
29 #define XFEATURE_MASK_TEST_SUPPORTED	\
30 	((1 << XFEATURE_YMM) |		\
31 	 (1 << XFEATURE_OPMASK)	|	\
32 	 (1 << XFEATURE_ZMM_Hi256) |	\
33 	 (1 << XFEATURE_Hi16_ZMM) |	\
34 	 (1 << XFEATURE_XTILEDATA) |	\
35 	 (1 << XFEATURE_APX))
36 
37 
38 static struct xstate_info xstate;
39 
40 struct futex_info {
41 	unsigned int iterations;
42 	struct futex_info *next;
43 	pthread_mutex_t mutex;
44 	pthread_t thread;
45 	bool valid;
46 	int nr;
47 };
48 
load_rand_xstate(struct xstate_info * xstate,struct xsave_buffer * xbuf)49 static inline void load_rand_xstate(struct xstate_info *xstate, struct xsave_buffer *xbuf)
50 {
51 	clear_xstate_header(xbuf);
52 	set_xstatebv(xbuf, xstate->mask);
53 	set_rand_data(xstate, xbuf);
54 	xrstor(xbuf, xstate->mask);
55 }
56 
load_init_xstate(struct xstate_info * xstate,struct xsave_buffer * xbuf)57 static inline void load_init_xstate(struct xstate_info *xstate, struct xsave_buffer *xbuf)
58 {
59 	clear_xstate_header(xbuf);
60 	xrstor(xbuf, xstate->mask);
61 }
62 
copy_xstate(struct xsave_buffer * xbuf_dst,struct xsave_buffer * xbuf_src)63 static inline void copy_xstate(struct xsave_buffer *xbuf_dst, struct xsave_buffer *xbuf_src)
64 {
65 	memcpy(&xbuf_dst->bytes[xstate.xbuf_offset],
66 	       &xbuf_src->bytes[xstate.xbuf_offset],
67 	       xstate.size);
68 }
69 
validate_xstate_same(struct xsave_buffer * xbuf1,struct xsave_buffer * xbuf2)70 static inline bool validate_xstate_same(struct xsave_buffer *xbuf1, struct xsave_buffer *xbuf2)
71 {
72 	int ret;
73 
74 	ret = memcmp(&xbuf1->bytes[xstate.xbuf_offset],
75 		     &xbuf2->bytes[xstate.xbuf_offset],
76 		     xstate.size);
77 	return ret == 0;
78 }
79 
validate_xregs_same(struct xsave_buffer * xbuf1)80 static inline bool validate_xregs_same(struct xsave_buffer *xbuf1)
81 {
82 	struct xsave_buffer *xbuf2;
83 	bool ret;
84 
85 	xbuf2 = alloc_xbuf();
86 	if (!xbuf2)
87 		ksft_exit_fail_msg("failed to allocate XSAVE buffer\n");
88 
89 	xsave(xbuf2, xstate.mask);
90 	ret = validate_xstate_same(xbuf1, xbuf2);
91 
92 	free(xbuf2);
93 	return ret;
94 }
95 
96 /* Context switching test */
97 
check_xstate(void * info)98 static void *check_xstate(void *info)
99 {
100 	struct futex_info *finfo = (struct futex_info *)info;
101 	struct xsave_buffer *xbuf;
102 	int i;
103 
104 	xbuf = alloc_xbuf();
105 	if (!xbuf)
106 		ksft_exit_fail_msg("unable to allocate XSAVE buffer\n");
107 
108 	/*
109 	 * Load random data into 'xbuf' and then restore it to the xstate
110 	 * registers.
111 	 */
112 	load_rand_xstate(&xstate, xbuf);
113 	finfo->valid = true;
114 
115 	for (i = 0; i < finfo->iterations; i++) {
116 		pthread_mutex_lock(&finfo->mutex);
117 
118 		/*
119 		 * Ensure the register values have not diverged from the
120 		 * record. Then reload a new random value.  If it failed
121 		 * ever before, skip it.
122 		 */
123 		if (finfo->valid) {
124 			finfo->valid = validate_xregs_same(xbuf);
125 			load_rand_xstate(&xstate, xbuf);
126 		}
127 
128 		/*
129 		 * The last thread's last unlock will be for thread 0's
130 		 * mutex. However, thread 0 will have already exited the
131 		 * loop and the mutex will already be unlocked.
132 		 *
133 		 * Because this is not an ERRORCHECK mutex, that
134 		 * inconsistency will be silently ignored.
135 		 */
136 		pthread_mutex_unlock(&finfo->next->mutex);
137 	}
138 
139 	free(xbuf);
140 	return finfo;
141 }
142 
create_threads(uint32_t num_threads,uint32_t iterations,struct futex_info * finfo)143 static void create_threads(uint32_t num_threads, uint32_t iterations, struct futex_info *finfo)
144 {
145 	int i;
146 
147 	for (i = 0; i < num_threads; i++) {
148 		int next_nr;
149 
150 		finfo[i].nr = i;
151 		finfo[i].iterations = iterations;
152 
153 		/*
154 		 * Thread 'i' will wait on this mutex to be unlocked.
155 		 * Lock it immediately after initialization:
156 		 */
157 		pthread_mutex_init(&finfo[i].mutex, NULL);
158 		pthread_mutex_lock(&finfo[i].mutex);
159 
160 		next_nr = (i + 1) % num_threads;
161 		finfo[i].next = &finfo[next_nr];
162 
163 		if (pthread_create(&finfo[i].thread, NULL, check_xstate, &finfo[i]))
164 			ksft_exit_fail_msg("pthread_create() failed\n");
165 	}
166 }
167 
checkout_threads(uint32_t num_threads,struct futex_info * finfo)168 static bool checkout_threads(uint32_t num_threads, struct futex_info *finfo)
169 {
170 	void *thread_retval;
171 	bool valid = true;
172 	int err, i;
173 
174 	for (i = 0; i < num_threads; i++) {
175 		err = pthread_join(finfo[i].thread, &thread_retval);
176 		if (err)
177 			ksft_exit_fail_msg("pthread_join() failed for thread %d err: %d\n", i, err);
178 
179 		if (thread_retval != &finfo[i]) {
180 			ksft_exit_fail_msg("unexpected thread retval for thread %d: %p\n",
181 					   i, thread_retval);
182 		}
183 
184 		valid &= finfo[i].valid;
185 	}
186 
187 	return valid;
188 }
189 
affinitize_cpu0(void)190 static void affinitize_cpu0(void)
191 {
192 	cpu_set_t cpuset;
193 
194 	CPU_ZERO(&cpuset);
195 	CPU_SET(0, &cpuset);
196 
197 	if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0)
198 		ksft_exit_fail_msg("sched_setaffinity to CPU 0 failed\n");
199 }
200 
test_context_switch(uint32_t num_threads,uint32_t iterations)201 static void test_context_switch(uint32_t num_threads, uint32_t iterations)
202 {
203 	struct futex_info *finfo;
204 
205 	/* Affinitize to one CPU to force context switches */
206 	affinitize_cpu0();
207 
208 	printf("[RUN]\t%s: check context switches, %d iterations, %d threads.\n",
209 	       xstate.name, iterations, num_threads);
210 
211 	finfo = malloc(sizeof(*finfo) * num_threads);
212 	if (!finfo)
213 		ksft_exit_fail_msg("unable allocate memory\n");
214 
215 	create_threads(num_threads, iterations, finfo);
216 
217 	/*
218 	 * This thread wakes up thread 0
219 	 * Thread 0 will wake up 1
220 	 * Thread 1 will wake up 2
221 	 * ...
222 	 * The last thread will wake up 0
223 	 *
224 	 * This will repeat for the configured
225 	 * number of iterations.
226 	 */
227 	pthread_mutex_unlock(&finfo[0].mutex);
228 
229 	/* Wait for all the threads to finish: */
230 	if (checkout_threads(num_threads, finfo))
231 		printf("[OK]\tNo incorrect case was found.\n");
232 	else
233 		printf("[FAIL]\tFailed with context switching test.\n");
234 
235 	free(finfo);
236 }
237 
238 /*
239  * Ptrace test for the ABI format as described in arch/x86/include/asm/user.h
240  */
241 
242 /*
243  * Make sure the ptracee has the expanded kernel buffer on the first use.
244  * Then, initialize the state before performing the state injection from
245  * the ptracer. For non-dynamic states, this is benign.
246  */
ptracee_touch_xstate(void)247 static inline void ptracee_touch_xstate(void)
248 {
249 	struct xsave_buffer *xbuf;
250 
251 	xbuf = alloc_xbuf();
252 
253 	load_rand_xstate(&xstate, xbuf);
254 	load_init_xstate(&xstate, xbuf);
255 
256 	free(xbuf);
257 }
258 
259 /*
260  * Ptracer injects the randomized xstate data. It also reads before and
261  * after that, which will execute the kernel's state copy functions.
262  */
ptracer_inject_xstate(pid_t target)263 static void ptracer_inject_xstate(pid_t target)
264 {
265 	uint32_t xbuf_size = get_xbuf_size();
266 	struct xsave_buffer *xbuf1, *xbuf2;
267 	struct iovec iov;
268 
269 	/*
270 	 * Allocate buffers to keep data while ptracer can write the
271 	 * other buffer
272 	 */
273 	xbuf1 = alloc_xbuf();
274 	xbuf2 = alloc_xbuf();
275 	if (!xbuf1 || !xbuf2)
276 		ksft_exit_fail_msg("unable to allocate XSAVE buffer\n");
277 
278 	iov.iov_base = xbuf1;
279 	iov.iov_len  = xbuf_size;
280 
281 	if (ptrace(PTRACE_GETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
282 		ksft_exit_fail_msg("PTRACE_GETREGSET failed\n");
283 
284 	printf("[RUN]\t%s: inject xstate via ptrace().\n", xstate.name);
285 
286 	load_rand_xstate(&xstate, xbuf1);
287 	copy_xstate(xbuf2, xbuf1);
288 
289 	if (ptrace(PTRACE_SETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
290 		ksft_exit_fail_msg("PTRACE_SETREGSET failed\n");
291 
292 	if (ptrace(PTRACE_GETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
293 		ksft_exit_fail_msg("PTRACE_GETREGSET failed\n");
294 
295 	if (*(uint64_t *)get_fpx_sw_bytes(xbuf1) == xgetbv(0))
296 		printf("[OK]\t'xfeatures' in SW reserved area was correctly written\n");
297 	else
298 		printf("[FAIL]\t'xfeatures' in SW reserved area was not correctly written\n");
299 
300 	if (validate_xstate_same(xbuf2, xbuf1))
301 		printf("[OK]\txstate was correctly updated.\n");
302 	else
303 		printf("[FAIL]\txstate was not correctly updated.\n");
304 
305 	free(xbuf1);
306 	free(xbuf2);
307 }
308 
test_ptrace(void)309 static void test_ptrace(void)
310 {
311 	pid_t child;
312 	int status;
313 
314 	child = fork();
315 	if (child < 0) {
316 		ksft_exit_fail_msg("fork() failed\n");
317 	} else if (!child) {
318 		if (ptrace(PTRACE_TRACEME, 0, NULL, NULL))
319 			ksft_exit_fail_msg("PTRACE_TRACEME failed\n");
320 
321 		ptracee_touch_xstate();
322 
323 		raise(SIGTRAP);
324 		_exit(0);
325 	}
326 
327 	do {
328 		wait(&status);
329 	} while (WSTOPSIG(status) != SIGTRAP);
330 
331 	ptracer_inject_xstate(child);
332 
333 	ptrace(PTRACE_DETACH, child, NULL, NULL);
334 	wait(&status);
335 	if (!WIFEXITED(status) || WEXITSTATUS(status))
336 		ksft_exit_fail_msg("ptracee exit error\n");
337 }
338 
339 /*
340  * Test signal delivery for the ABI compatibility.
341  * See the ABI format: arch/x86/include/uapi/asm/sigcontext.h
342  */
343 
344 /*
345  * Avoid using printf() in signal handlers as it is not
346  * async-signal-safe.
347  */
348 #define SIGNAL_BUF_LEN 1000
349 static char signal_message_buffer[SIGNAL_BUF_LEN];
sig_print(char * msg)350 static void sig_print(char *msg)
351 {
352 	int left = SIGNAL_BUF_LEN - strlen(signal_message_buffer) - 1;
353 
354 	strncat(signal_message_buffer, msg, left);
355 }
356 
357 static struct xsave_buffer *stashed_xbuf;
358 
validate_sigfpstate(int sig,siginfo_t * si,void * ctx_void)359 static void validate_sigfpstate(int sig, siginfo_t *si, void *ctx_void)
360 {
361 	ucontext_t *ctx = (ucontext_t *)ctx_void;
362 	void *xbuf = ctx->uc_mcontext.fpregs;
363 	struct _fpx_sw_bytes *sw_bytes;
364 	uint32_t magic2;
365 
366 	/* Reset the signal message buffer: */
367 	signal_message_buffer[0] = '\0';
368 
369 	sw_bytes = get_fpx_sw_bytes(xbuf);
370 	if (sw_bytes->magic1 == FP_XSTATE_MAGIC1)
371 		sig_print("[OK]\t'magic1' is valid\n");
372 	else
373 		sig_print("[FAIL]\t'magic1' is not valid\n");
374 
375 	if (get_fpx_sw_bytes_features(xbuf) & xstate.mask)
376 		sig_print("[OK]\t'xfeatures' in SW reserved area is valid\n");
377 	else
378 		sig_print("[FAIL]\t'xfeatures' in SW reserved area is not valid\n");
379 
380 	if (get_xstatebv(xbuf) & xstate.mask)
381 		sig_print("[OK]\t'xfeatures' in XSAVE header is valid\n");
382 	else
383 		sig_print("[FAIL]\t'xfeatures' in XSAVE header is not valid\n");
384 
385 	if (validate_xstate_same(stashed_xbuf, xbuf))
386 		sig_print("[OK]\txstate delivery was successful\n");
387 	else
388 		sig_print("[FAIL]\txstate delivery was not successful\n");
389 
390 	magic2 = *(uint32_t *)(xbuf + sw_bytes->xstate_size);
391 	if (magic2 == FP_XSTATE_MAGIC2)
392 		sig_print("[OK]\t'magic2' is valid\n");
393 	else
394 		sig_print("[FAIL]\t'magic2' is not valid\n");
395 
396 	set_rand_data(&xstate, xbuf);
397 	copy_xstate(stashed_xbuf, xbuf);
398 }
399 
test_signal(void)400 static void test_signal(void)
401 {
402 	bool valid_xstate;
403 
404 	/*
405 	 * The signal handler will access this to verify xstate context
406 	 * preservation.
407 	 */
408 	stashed_xbuf = alloc_xbuf();
409 	if (!stashed_xbuf)
410 		ksft_exit_fail_msg("unable to allocate XSAVE buffer\n");
411 
412 	printf("[RUN]\t%s: load xstate and raise SIGUSR1\n", xstate.name);
413 
414 	sethandler(SIGUSR1, validate_sigfpstate, 0);
415 
416 	load_rand_xstate(&xstate, stashed_xbuf);
417 
418 	raise(SIGUSR1);
419 
420 	/*
421 	 * Immediately record the test result, deferring printf() to
422 	 * prevent unintended state contamination by that.
423 	 */
424 	valid_xstate = validate_xregs_same(stashed_xbuf);
425 	printf("%s", signal_message_buffer);
426 
427 	printf("[RUN]\t%s: load new xstate from sighandler and check it after sigreturn\n",
428 	       xstate.name);
429 
430 	if (valid_xstate)
431 		printf("[OK]\txstate was restored correctly\n");
432 	else
433 		printf("[FAIL]\txstate restoration failed\n");
434 
435 	clearhandler(SIGUSR1);
436 	free(stashed_xbuf);
437 }
438 
test_xstate(uint32_t feature_num)439 void test_xstate(uint32_t feature_num)
440 {
441 	const unsigned int ctxtsw_num_threads = 5, ctxtsw_iterations = 10;
442 	unsigned long features;
443 	long rc;
444 
445 	if (!(XFEATURE_MASK_TEST_SUPPORTED & (1 << feature_num))) {
446 		ksft_print_msg("The xstate test does not fully support the component %u, yet.\n",
447 			       feature_num);
448 		return;
449 	}
450 
451 	rc = syscall(SYS_arch_prctl, ARCH_GET_XCOMP_SUPP, &features);
452 	if (rc || !(features & (1 << feature_num))) {
453 		ksft_print_msg("The kernel does not support feature number: %u\n", feature_num);
454 		return;
455 	}
456 
457 	xstate = get_xstate_info(feature_num);
458 	if (!xstate.size || !xstate.xbuf_offset) {
459 		ksft_exit_fail_msg("invalid state size/offset (%d/%d)\n",
460 				   xstate.size, xstate.xbuf_offset);
461 	}
462 
463 	test_context_switch(ctxtsw_num_threads, ctxtsw_iterations);
464 	test_ptrace();
465 	test_signal();
466 }
467