Skip to content

Commit 7cb2fbe

Browse files
ChangSeokBaeIngo Molnar
authored andcommitted
selftests/x86/xstate: Refactor ptrace ABI test
Following the refactoring of the context switching test, the ptrace test is another component reusable for other xstate features. As part of this restructuring, add a missing check to validate the user_xstateregs->xstate_fx_sw field in the ABI. Also, replace err() and fatal_error() with ksft_exit_fail_msg() for consistency in error handling. Expected output: $ amx_64 ... [RUN] AMX Tile data: inject xstate via ptrace(). [OK] 'xfeatures' in SW reserved area was correctly written [OK] xstate was correctly updated. Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com> Signed-off-by: Ingo Molnar <mingo@kernel.org> Link: https://lore.kernel.org/r/20250226010731.2456-6-chang.seok.bae@intel.com
1 parent 40f6852 commit 7cb2fbe

File tree

3 files changed

+131
-107
lines changed

3 files changed

+131
-107
lines changed

tools/testing/selftests/x86/amx.c

Lines changed: 1 addition & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,8 @@
1313
#include <sys/auxv.h>
1414
#include <sys/mman.h>
1515
#include <sys/shm.h>
16-
#include <sys/ptrace.h>
1716
#include <sys/syscall.h>
1817
#include <sys/wait.h>
19-
#include <sys/uio.h>
2018

2119
#include "helpers.h"
2220
#include "xstate.h"
@@ -32,8 +30,6 @@
3230
#define XFEATURE_MASK_XTILEDATA (1 << XFEATURE_XTILEDATA)
3331
#define XFEATURE_MASK_XTILE (XFEATURE_MASK_XTILECFG | XFEATURE_MASK_XTILEDATA)
3432

35-
static uint32_t xbuf_size;
36-
3733
struct xstate_info xtiledata;
3834

3935
/* The helpers for managing XSAVE buffer and tile states: */
@@ -154,13 +150,6 @@ static inline bool load_rand_tiledata(struct xsave_buffer *xbuf)
154150
return xrstor_safe(xbuf, XFEATURE_MASK_XTILEDATA);
155151
}
156152

157-
/* Return XTILEDATA to its initial configuration. */
158-
static inline void init_xtiledata(void)
159-
{
160-
clear_xstate_header(stashed_xsave);
161-
xrstor_safe(stashed_xsave, XFEATURE_MASK_XTILEDATA);
162-
}
163-
164153
enum expected_result { FAIL_EXPECTED, SUCCESS_EXPECTED };
165154

166155
/* arch_prctl() and sigaltstack() test */
@@ -489,99 +478,6 @@ static void test_fork(void)
489478
_exit(0);
490479
}
491480

492-
/* Ptrace test */
493-
494-
/*
495-
* Make sure the ptracee has the expanded kernel buffer on the first
496-
* use. Then, initialize the state before performing the state
497-
* injection from the ptracer.
498-
*/
499-
static inline void ptracee_firstuse_tiledata(void)
500-
{
501-
load_rand_tiledata(stashed_xsave);
502-
init_xtiledata();
503-
}
504-
505-
/*
506-
* Ptracer injects the randomized tile data state. It also reads
507-
* before and after that, which will execute the kernel's state copy
508-
* functions. So, the tester is advised to double-check any emitted
509-
* kernel messages.
510-
*/
511-
static void ptracer_inject_tiledata(pid_t target)
512-
{
513-
struct xsave_buffer *xbuf;
514-
struct iovec iov;
515-
516-
xbuf = alloc_xbuf();
517-
if (!xbuf)
518-
fatal_error("unable to allocate XSAVE buffer");
519-
520-
printf("\tRead the init'ed tiledata via ptrace().\n");
521-
522-
iov.iov_base = xbuf;
523-
iov.iov_len = xbuf_size;
524-
525-
memset(stashed_xsave, 0, xbuf_size);
526-
527-
if (ptrace(PTRACE_GETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
528-
fatal_error("PTRACE_GETREGSET");
529-
530-
if (!__compare_tiledata_state(stashed_xsave, xbuf))
531-
printf("[OK]\tThe init'ed tiledata was read from ptracee.\n");
532-
else
533-
printf("[FAIL]\tThe init'ed tiledata was not read from ptracee.\n");
534-
535-
printf("\tInject tiledata via ptrace().\n");
536-
537-
load_rand_tiledata(xbuf);
538-
539-
memcpy(&stashed_xsave->bytes[xtiledata.xbuf_offset],
540-
&xbuf->bytes[xtiledata.xbuf_offset],
541-
xtiledata.size);
542-
543-
if (ptrace(PTRACE_SETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
544-
fatal_error("PTRACE_SETREGSET");
545-
546-
if (ptrace(PTRACE_GETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
547-
fatal_error("PTRACE_GETREGSET");
548-
549-
if (!__compare_tiledata_state(stashed_xsave, xbuf))
550-
printf("[OK]\tTiledata was correctly written to ptracee.\n");
551-
else
552-
printf("[FAIL]\tTiledata was not correctly written to ptracee.\n");
553-
}
554-
555-
static void test_ptrace(void)
556-
{
557-
pid_t child;
558-
int status;
559-
560-
child = fork();
561-
if (child < 0) {
562-
err(1, "fork");
563-
} else if (!child) {
564-
if (ptrace(PTRACE_TRACEME, 0, NULL, NULL))
565-
err(1, "PTRACE_TRACEME");
566-
567-
ptracee_firstuse_tiledata();
568-
569-
raise(SIGTRAP);
570-
_exit(0);
571-
}
572-
573-
do {
574-
wait(&status);
575-
} while (WSTOPSIG(status) != SIGTRAP);
576-
577-
ptracer_inject_tiledata(child);
578-
579-
ptrace(PTRACE_DETACH, child, NULL, NULL);
580-
wait(&status);
581-
if (!WIFEXITED(status) || WEXITSTATUS(status))
582-
err(1, "ptrace test");
583-
}
584-
585481
int main(void)
586482
{
587483
const unsigned int ctxtsw_num_threads = 5, ctxtsw_iterations = 10;
@@ -594,8 +490,6 @@ int main(void)
594490
return KSFT_SKIP;
595491
}
596492

597-
xbuf_size = get_xbuf_size();
598-
599493
xtiledata = get_xstate_info(XFEATURE_XTILEDATA);
600494
if (!xtiledata.size || !xtiledata.xbuf_offset) {
601495
fatal_error("xstate cpuid: invalid tile data size/offset: %d/%d",
@@ -614,7 +508,7 @@ int main(void)
614508

615509
test_context_switch(XFEATURE_XTILEDATA, ctxtsw_num_threads, ctxtsw_iterations);
616510

617-
test_ptrace();
511+
test_ptrace(XFEATURE_XTILEDATA);
618512

619513
clearhandler(SIGILL);
620514
free_stashed_xsave();

tools/testing/selftests/x86/xstate.c

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,25 @@
22

33
#define _GNU_SOURCE
44

5+
#include <elf.h>
56
#include <pthread.h>
67
#include <stdbool.h>
78

9+
#include <sys/ptrace.h>
10+
#include <sys/uio.h>
11+
#include <sys/wait.h>
12+
813
#include "helpers.h"
914
#include "xstate.h"
1015

16+
static inline uint64_t xgetbv(uint32_t index)
17+
{
18+
uint32_t eax, edx;
19+
20+
asm volatile("xgetbv" : "=a" (eax), "=d" (edx) : "c" (index));
21+
return eax + ((uint64_t)edx << 32);
22+
}
23+
1124
static struct xstate_info xstate;
1225

1326
struct futex_info {
@@ -27,6 +40,19 @@ static inline void load_rand_xstate(struct xstate_info *xstate, struct xsave_buf
2740
xrstor(xbuf, xstate->mask);
2841
}
2942

43+
static inline void load_init_xstate(struct xstate_info *xstate, struct xsave_buffer *xbuf)
44+
{
45+
clear_xstate_header(xbuf);
46+
xrstor(xbuf, xstate->mask);
47+
}
48+
49+
static inline void copy_xstate(struct xsave_buffer *xbuf_dst, struct xsave_buffer *xbuf_src)
50+
{
51+
memcpy(&xbuf_dst->bytes[xstate.xbuf_offset],
52+
&xbuf_src->bytes[xstate.xbuf_offset],
53+
xstate.size);
54+
}
55+
3056
static inline bool validate_xstate_same(struct xsave_buffer *xbuf1, struct xsave_buffer *xbuf2)
3157
{
3258
int ret;
@@ -196,3 +222,106 @@ void test_context_switch(uint32_t feature_num, uint32_t num_threads, uint32_t it
196222

197223
free(finfo);
198224
}
225+
226+
/*
227+
* Ptrace test for the ABI format as described in arch/x86/include/asm/user.h
228+
*/
229+
230+
/*
231+
* Make sure the ptracee has the expanded kernel buffer on the first use.
232+
* Then, initialize the state before performing the state injection from
233+
* the ptracer. For non-dynamic states, this is benign.
234+
*/
235+
static inline void ptracee_touch_xstate(void)
236+
{
237+
struct xsave_buffer *xbuf;
238+
239+
xbuf = alloc_xbuf();
240+
241+
load_rand_xstate(&xstate, xbuf);
242+
load_init_xstate(&xstate, xbuf);
243+
244+
free(xbuf);
245+
}
246+
247+
/*
248+
* Ptracer injects the randomized xstate data. It also reads before and
249+
* after that, which will execute the kernel's state copy functions.
250+
*/
251+
static void ptracer_inject_xstate(pid_t target)
252+
{
253+
uint32_t xbuf_size = get_xbuf_size();
254+
struct xsave_buffer *xbuf1, *xbuf2;
255+
struct iovec iov;
256+
257+
/*
258+
* Allocate buffers to keep data while ptracer can write the
259+
* other buffer
260+
*/
261+
xbuf1 = alloc_xbuf();
262+
xbuf2 = alloc_xbuf();
263+
if (!xbuf1 || !xbuf2)
264+
ksft_exit_fail_msg("unable to allocate XSAVE buffer\n");
265+
266+
iov.iov_base = xbuf1;
267+
iov.iov_len = xbuf_size;
268+
269+
if (ptrace(PTRACE_GETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
270+
ksft_exit_fail_msg("PTRACE_GETREGSET failed\n");
271+
272+
printf("[RUN]\t%s: inject xstate via ptrace().\n", xstate.name);
273+
274+
load_rand_xstate(&xstate, xbuf1);
275+
copy_xstate(xbuf2, xbuf1);
276+
277+
if (ptrace(PTRACE_SETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
278+
ksft_exit_fail_msg("PTRACE_SETREGSET failed\n");
279+
280+
if (ptrace(PTRACE_GETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
281+
ksft_exit_fail_msg("PTRACE_GETREGSET failed\n");
282+
283+
if (*(uint64_t *)get_fpx_sw_bytes(xbuf1) == xgetbv(0))
284+
printf("[OK]\t'xfeatures' in SW reserved area was correctly written\n");
285+
else
286+
printf("[FAIL]\t'xfeatures' in SW reserved area was not correctly written\n");
287+
288+
if (validate_xstate_same(xbuf2, xbuf1))
289+
printf("[OK]\txstate was correctly updated.\n");
290+
else
291+
printf("[FAIL]\txstate was not correctly updated.\n");
292+
293+
free(xbuf1);
294+
free(xbuf2);
295+
}
296+
297+
void test_ptrace(uint32_t feature_num)
298+
{
299+
pid_t child;
300+
int status;
301+
302+
xstate = get_xstate_info(feature_num);
303+
304+
child = fork();
305+
if (child < 0) {
306+
ksft_exit_fail_msg("fork() failed\n");
307+
} else if (!child) {
308+
if (ptrace(PTRACE_TRACEME, 0, NULL, NULL))
309+
ksft_exit_fail_msg("PTRACE_TRACEME failed\n");
310+
311+
ptracee_touch_xstate();
312+
313+
raise(SIGTRAP);
314+
_exit(0);
315+
}
316+
317+
do {
318+
wait(&status);
319+
} while (WSTOPSIG(status) != SIGTRAP);
320+
321+
ptracer_inject_xstate(child);
322+
323+
ptrace(PTRACE_DETACH, child, NULL, NULL);
324+
wait(&status);
325+
if (!WIFEXITED(status) || WEXITSTATUS(status))
326+
ksft_exit_fail_msg("ptracee exit error\n");
327+
}

tools/testing/selftests/x86/xstate.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,5 +190,6 @@ static inline void set_rand_data(struct xstate_info *xstate, struct xsave_buffer
190190
}
191191

192192
void test_context_switch(uint32_t feature_num, uint32_t num_threads, uint32_t iterations);
193+
void test_ptrace(uint32_t feature_num);
193194

194195
#endif /* __SELFTESTS_X86_XSTATE_H */

0 commit comments

Comments
 (0)