1 // SPDX-License-Identifier: GPL-2.0
2
3 #define _GNU_SOURCE
4 #include <err.h>
5 #include <errno.h>
6 #include <setjmp.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <stdbool.h>
10 #include <unistd.h>
11 #include <x86intrin.h>
12
13 #include <sys/auxv.h>
14 #include <sys/mman.h>
15 #include <sys/shm.h>
16 #include <sys/syscall.h>
17 #include <sys/wait.h>
18
19 #include "helpers.h"
20 #include "xstate.h"
21
22 #ifndef __x86_64__
23 # error This test is 64-bit only
24 #endif
25
26 /* err() exits and will not return */
27 #define fatal_error(msg, ...) err(1, "[FAIL]\t" msg, ##__VA_ARGS__)
28
29 #define XFEATURE_MASK_XTILECFG (1 << XFEATURE_XTILECFG)
30 #define XFEATURE_MASK_XTILEDATA (1 << XFEATURE_XTILEDATA)
31 #define XFEATURE_MASK_XTILE (XFEATURE_MASK_XTILECFG | XFEATURE_MASK_XTILEDATA)
32
33 struct xstate_info xtiledata;
34
35 /* The helpers for managing XSAVE buffer and tile states: */
36
37 struct xsave_buffer *stashed_xsave;
38
init_stashed_xsave(void)39 static void init_stashed_xsave(void)
40 {
41 stashed_xsave = alloc_xbuf();
42 if (!stashed_xsave)
43 fatal_error("failed to allocate stashed_xsave\n");
44 clear_xstate_header(stashed_xsave);
45 }
46
free_stashed_xsave(void)47 static void free_stashed_xsave(void)
48 {
49 free(stashed_xsave);
50 }
51
52 /* Work around printf() being unsafe in signals: */
53 #define SIGNAL_BUF_LEN 1000
54 char signal_message_buffer[SIGNAL_BUF_LEN];
sig_print(char * msg)55 void sig_print(char *msg)
56 {
57 int left = SIGNAL_BUF_LEN - strlen(signal_message_buffer) - 1;
58
59 strncat(signal_message_buffer, msg, left);
60 }
61
62 static volatile bool noperm_signaled;
63 static int noperm_errs;
64 /*
65 * Signal handler for when AMX is used but
66 * permission has not been obtained.
67 */
handle_noperm(int sig,siginfo_t * si,void * ctx_void)68 static void handle_noperm(int sig, siginfo_t *si, void *ctx_void)
69 {
70 ucontext_t *ctx = (ucontext_t *)ctx_void;
71 void *xbuf = ctx->uc_mcontext.fpregs;
72 struct _fpx_sw_bytes *sw_bytes;
73 uint64_t features;
74
75 /* Reset the signal message buffer: */
76 signal_message_buffer[0] = '\0';
77 sig_print("\tAt SIGILL handler,\n");
78
79 if (si->si_code != ILL_ILLOPC) {
80 noperm_errs++;
81 sig_print("[FAIL]\tInvalid signal code.\n");
82 } else {
83 sig_print("[OK]\tValid signal code (ILL_ILLOPC).\n");
84 }
85
86 sw_bytes = get_fpx_sw_bytes(xbuf);
87 /*
88 * Without permission, the signal XSAVE buffer should not
89 * have room for AMX register state (aka. xtiledata).
90 * Check that the size does not overlap with where xtiledata
91 * will reside.
92 *
93 * This also implies that no state components *PAST*
94 * XTILEDATA (features >=19) can be present in the buffer.
95 */
96 if (sw_bytes->xstate_size <= xtiledata.xbuf_offset) {
97 sig_print("[OK]\tValid xstate size\n");
98 } else {
99 noperm_errs++;
100 sig_print("[FAIL]\tInvalid xstate size\n");
101 }
102
103 features = get_fpx_sw_bytes_features(xbuf);
104 /*
105 * Without permission, the XTILEDATA feature
106 * bit should not be set.
107 */
108 if ((features & XFEATURE_MASK_XTILEDATA) == 0) {
109 sig_print("[OK]\tValid xstate mask\n");
110 } else {
111 noperm_errs++;
112 sig_print("[FAIL]\tInvalid xstate mask\n");
113 }
114
115 noperm_signaled = true;
116 ctx->uc_mcontext.gregs[REG_RIP] += 3; /* Skip the faulting XRSTOR */
117 }
118
119 /* Return true if XRSTOR is successful; otherwise, false. */
xrstor_safe(struct xsave_buffer * xbuf,uint64_t mask)120 static inline bool xrstor_safe(struct xsave_buffer *xbuf, uint64_t mask)
121 {
122 noperm_signaled = false;
123 xrstor(xbuf, mask);
124
125 /* Print any messages produced by the signal code: */
126 printf("%s", signal_message_buffer);
127 /*
128 * Reset the buffer to make sure any future printing
129 * only outputs new messages:
130 */
131 signal_message_buffer[0] = '\0';
132
133 if (noperm_errs)
134 fatal_error("saw %d errors in noperm signal handler\n", noperm_errs);
135
136 return !noperm_signaled;
137 }
138
139 /*
140 * Use XRSTOR to populate the XTILEDATA registers with
141 * random data.
142 *
143 * Return true if successful; otherwise, false.
144 */
load_rand_tiledata(struct xsave_buffer * xbuf)145 static inline bool load_rand_tiledata(struct xsave_buffer *xbuf)
146 {
147 clear_xstate_header(xbuf);
148 set_xstatebv(xbuf, XFEATURE_MASK_XTILEDATA);
149 set_rand_data(&xtiledata, xbuf);
150 return xrstor_safe(xbuf, XFEATURE_MASK_XTILEDATA);
151 }
152
153 enum expected_result { FAIL_EXPECTED, SUCCESS_EXPECTED };
154
155 /* arch_prctl() and sigaltstack() test */
156
157 #define ARCH_GET_XCOMP_SUPP 0x1021
158 #define ARCH_GET_XCOMP_PERM 0x1022
159 #define ARCH_REQ_XCOMP_PERM 0x1023
160
req_xtiledata_perm(void)161 static void req_xtiledata_perm(void)
162 {
163 syscall(SYS_arch_prctl, ARCH_REQ_XCOMP_PERM, XFEATURE_XTILEDATA);
164 }
165
validate_req_xcomp_perm(enum expected_result exp)166 static void validate_req_xcomp_perm(enum expected_result exp)
167 {
168 unsigned long bitmask, expected_bitmask;
169 long rc;
170
171 rc = syscall(SYS_arch_prctl, ARCH_GET_XCOMP_PERM, &bitmask);
172 if (rc) {
173 fatal_error("prctl(ARCH_GET_XCOMP_PERM) error: %ld", rc);
174 } else if (!(bitmask & XFEATURE_MASK_XTILECFG)) {
175 fatal_error("ARCH_GET_XCOMP_PERM returns XFEATURE_XTILECFG off.");
176 }
177
178 rc = syscall(SYS_arch_prctl, ARCH_REQ_XCOMP_PERM, XFEATURE_XTILEDATA);
179 if (exp == FAIL_EXPECTED) {
180 if (rc) {
181 printf("[OK]\tARCH_REQ_XCOMP_PERM saw expected failure..\n");
182 return;
183 }
184
185 fatal_error("ARCH_REQ_XCOMP_PERM saw unexpected success.\n");
186 } else if (rc) {
187 fatal_error("ARCH_REQ_XCOMP_PERM saw unexpected failure.\n");
188 }
189
190 expected_bitmask = bitmask | XFEATURE_MASK_XTILEDATA;
191
192 rc = syscall(SYS_arch_prctl, ARCH_GET_XCOMP_PERM, &bitmask);
193 if (rc) {
194 fatal_error("prctl(ARCH_GET_XCOMP_PERM) error: %ld", rc);
195 } else if (bitmask != expected_bitmask) {
196 fatal_error("ARCH_REQ_XCOMP_PERM set a wrong bitmask: %lx, expected: %lx.\n",
197 bitmask, expected_bitmask);
198 } else {
199 printf("\tARCH_REQ_XCOMP_PERM is successful.\n");
200 }
201 }
202
validate_xcomp_perm(enum expected_result exp)203 static void validate_xcomp_perm(enum expected_result exp)
204 {
205 bool load_success = load_rand_tiledata(stashed_xsave);
206
207 if (exp == FAIL_EXPECTED) {
208 if (load_success) {
209 noperm_errs++;
210 printf("[FAIL]\tLoad tiledata succeeded.\n");
211 } else {
212 printf("[OK]\tLoad tiledata failed.\n");
213 }
214 } else if (exp == SUCCESS_EXPECTED) {
215 if (load_success) {
216 printf("[OK]\tLoad tiledata succeeded.\n");
217 } else {
218 noperm_errs++;
219 printf("[FAIL]\tLoad tiledata failed.\n");
220 }
221 }
222 }
223
224 #ifndef AT_MINSIGSTKSZ
225 # define AT_MINSIGSTKSZ 51
226 #endif
227
alloc_altstack(unsigned int size)228 static void *alloc_altstack(unsigned int size)
229 {
230 void *altstack;
231
232 altstack = mmap(NULL, size, PROT_READ | PROT_WRITE,
233 MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
234
235 if (altstack == MAP_FAILED)
236 fatal_error("mmap() for altstack");
237
238 return altstack;
239 }
240
setup_altstack(void * addr,unsigned long size,enum expected_result exp)241 static void setup_altstack(void *addr, unsigned long size, enum expected_result exp)
242 {
243 stack_t ss;
244 int rc;
245
246 memset(&ss, 0, sizeof(ss));
247 ss.ss_size = size;
248 ss.ss_sp = addr;
249
250 rc = sigaltstack(&ss, NULL);
251
252 if (exp == FAIL_EXPECTED) {
253 if (rc) {
254 printf("[OK]\tsigaltstack() failed.\n");
255 } else {
256 fatal_error("sigaltstack() succeeded unexpectedly.\n");
257 }
258 } else if (rc) {
259 fatal_error("sigaltstack()");
260 }
261 }
262
test_dynamic_sigaltstack(void)263 static void test_dynamic_sigaltstack(void)
264 {
265 unsigned int small_size, enough_size;
266 unsigned long minsigstksz;
267 void *altstack;
268
269 minsigstksz = getauxval(AT_MINSIGSTKSZ);
270 printf("\tAT_MINSIGSTKSZ = %lu\n", minsigstksz);
271 /*
272 * getauxval() itself can return 0 for failure or
273 * success. But, in this case, AT_MINSIGSTKSZ
274 * will always return a >=0 value if implemented.
275 * Just check for 0.
276 */
277 if (minsigstksz == 0) {
278 printf("no support for AT_MINSIGSTKSZ, skipping sigaltstack tests\n");
279 return;
280 }
281
282 enough_size = minsigstksz * 2;
283
284 altstack = alloc_altstack(enough_size);
285 printf("\tAllocate memory for altstack (%u bytes).\n", enough_size);
286
287 /*
288 * Try setup_altstack() with a size which can not fit
289 * XTILEDATA. ARCH_REQ_XCOMP_PERM should fail.
290 */
291 small_size = minsigstksz - xtiledata.size;
292 printf("\tAfter sigaltstack() with small size (%u bytes).\n", small_size);
293 setup_altstack(altstack, small_size, SUCCESS_EXPECTED);
294 validate_req_xcomp_perm(FAIL_EXPECTED);
295
296 /*
297 * Try setup_altstack() with a size derived from
298 * AT_MINSIGSTKSZ. It should be more than large enough
299 * and thus ARCH_REQ_XCOMP_PERM should succeed.
300 */
301 printf("\tAfter sigaltstack() with enough size (%u bytes).\n", enough_size);
302 setup_altstack(altstack, enough_size, SUCCESS_EXPECTED);
303 validate_req_xcomp_perm(SUCCESS_EXPECTED);
304
305 /*
306 * Try to coerce setup_altstack() to again accept a
307 * too-small altstack. This ensures that big-enough
308 * sigaltstacks can not shrink to a too-small value
309 * once XTILEDATA permission is established.
310 */
311 printf("\tThen, sigaltstack() with small size (%u bytes).\n", small_size);
312 setup_altstack(altstack, small_size, FAIL_EXPECTED);
313 }
314
test_dynamic_state(void)315 static void test_dynamic_state(void)
316 {
317 pid_t parent, child, grandchild;
318
319 parent = fork();
320 if (parent < 0) {
321 /* fork() failed */
322 fatal_error("fork");
323 } else if (parent > 0) {
324 int status;
325 /* fork() succeeded. Now in the parent. */
326
327 wait(&status);
328 if (!WIFEXITED(status) || WEXITSTATUS(status))
329 fatal_error("arch_prctl test parent exit");
330 return;
331 }
332 /* fork() succeeded. Now in the child . */
333
334 printf("[RUN]\tCheck ARCH_REQ_XCOMP_PERM around process fork() and sigaltack() test.\n");
335
336 printf("\tFork a child.\n");
337 child = fork();
338 if (child < 0) {
339 fatal_error("fork");
340 } else if (child > 0) {
341 int status;
342
343 wait(&status);
344 if (!WIFEXITED(status) || WEXITSTATUS(status))
345 fatal_error("arch_prctl test child exit");
346 _exit(0);
347 }
348
349 /*
350 * The permission request should fail without an
351 * XTILEDATA-compatible signal stack
352 */
353 printf("\tTest XCOMP_PERM at child.\n");
354 validate_xcomp_perm(FAIL_EXPECTED);
355
356 /*
357 * Set up an XTILEDATA-compatible signal stack and
358 * also obtain permission to populate XTILEDATA.
359 */
360 printf("\tTest dynamic sigaltstack at child:\n");
361 test_dynamic_sigaltstack();
362
363 /* Ensure that XTILEDATA can be populated. */
364 printf("\tTest XCOMP_PERM again at child.\n");
365 validate_xcomp_perm(SUCCESS_EXPECTED);
366
367 printf("\tFork a grandchild.\n");
368 grandchild = fork();
369 if (grandchild < 0) {
370 /* fork() failed */
371 fatal_error("fork");
372 } else if (!grandchild) {
373 /* fork() succeeded. Now in the (grand)child. */
374 printf("\tTest XCOMP_PERM at grandchild.\n");
375
376 /*
377 * Ensure that the grandchild inherited
378 * permission and a compatible sigaltstack:
379 */
380 validate_xcomp_perm(SUCCESS_EXPECTED);
381 } else {
382 int status;
383 /* fork() succeeded. Now in the parent. */
384
385 wait(&status);
386 if (!WIFEXITED(status) || WEXITSTATUS(status))
387 fatal_error("fork test grandchild");
388 }
389
390 _exit(0);
391 }
392
__compare_tiledata_state(struct xsave_buffer * xbuf1,struct xsave_buffer * xbuf2)393 static inline int __compare_tiledata_state(struct xsave_buffer *xbuf1, struct xsave_buffer *xbuf2)
394 {
395 return memcmp(&xbuf1->bytes[xtiledata.xbuf_offset],
396 &xbuf2->bytes[xtiledata.xbuf_offset],
397 xtiledata.size);
398 }
399
400 /*
401 * Save current register state and compare it to @xbuf1.'
402 *
403 * Returns false if @xbuf1 matches the registers.
404 * Returns true if @xbuf1 differs from the registers.
405 */
__validate_tiledata_regs(struct xsave_buffer * xbuf1)406 static inline bool __validate_tiledata_regs(struct xsave_buffer *xbuf1)
407 {
408 struct xsave_buffer *xbuf2;
409 int ret;
410
411 xbuf2 = alloc_xbuf();
412 if (!xbuf2)
413 fatal_error("failed to allocate XSAVE buffer\n");
414
415 xsave(xbuf2, XFEATURE_MASK_XTILEDATA);
416 ret = __compare_tiledata_state(xbuf1, xbuf2);
417
418 free(xbuf2);
419
420 if (ret == 0)
421 return false;
422 return true;
423 }
424
validate_tiledata_regs_changed(struct xsave_buffer * xbuf)425 static inline void validate_tiledata_regs_changed(struct xsave_buffer *xbuf)
426 {
427 int ret = __validate_tiledata_regs(xbuf);
428
429 if (ret == 0)
430 fatal_error("TILEDATA registers did not change");
431 }
432
433 /* tiledata inheritance test */
434
test_fork(void)435 static void test_fork(void)
436 {
437 pid_t child, grandchild;
438
439 child = fork();
440 if (child < 0) {
441 /* fork() failed */
442 fatal_error("fork");
443 } else if (child > 0) {
444 /* fork() succeeded. Now in the parent. */
445 int status;
446
447 wait(&status);
448 if (!WIFEXITED(status) || WEXITSTATUS(status))
449 fatal_error("fork test child");
450 return;
451 }
452 /* fork() succeeded. Now in the child. */
453 printf("[RUN]\tCheck tile data inheritance.\n\tBefore fork(), load tiledata\n");
454
455 load_rand_tiledata(stashed_xsave);
456
457 grandchild = fork();
458 if (grandchild < 0) {
459 /* fork() failed */
460 fatal_error("fork");
461 } else if (grandchild > 0) {
462 /* fork() succeeded. Still in the first child. */
463 int status;
464
465 wait(&status);
466 if (!WIFEXITED(status) || WEXITSTATUS(status))
467 fatal_error("fork test grand child");
468 _exit(0);
469 }
470 /* fork() succeeded. Now in the (grand)child. */
471
472 /*
473 * TILEDATA registers are not preserved across fork().
474 * Ensure that their value has changed:
475 */
476 validate_tiledata_regs_changed(stashed_xsave);
477
478 _exit(0);
479 }
480
main(void)481 int main(void)
482 {
483 unsigned long features;
484 long rc;
485
486 rc = syscall(SYS_arch_prctl, ARCH_GET_XCOMP_SUPP, &features);
487 if (rc || (features & XFEATURE_MASK_XTILE) != XFEATURE_MASK_XTILE) {
488 ksft_print_msg("no AMX support\n");
489 return KSFT_SKIP;
490 }
491
492 xtiledata = get_xstate_info(XFEATURE_XTILEDATA);
493 if (!xtiledata.size || !xtiledata.xbuf_offset) {
494 fatal_error("xstate cpuid: invalid tile data size/offset: %d/%d",
495 xtiledata.size, xtiledata.xbuf_offset);
496 }
497
498 init_stashed_xsave();
499 sethandler(SIGILL, handle_noperm, 0);
500
501 test_dynamic_state();
502
503 /* Request permission for the following tests */
504 req_xtiledata_perm();
505
506 test_fork();
507
508 /*
509 * Perform generic xstate tests for context switching, ptrace,
510 * and signal.
511 */
512 test_xstate(XFEATURE_XTILEDATA);
513
514 clearhandler(SIGILL);
515 free_stashed_xsave();
516
517 return 0;
518 }
519