Commit 64d3811e authored by Chang S. Bae's avatar Chang S. Bae Committed by Lin Wang
Browse files

selftests/x86/amx: Add a ptrace test

mainline inclusion
from mainline-v6.3-rc4
commit 62faca1c
category: bugfix
bugzilla: https://gitee.com/openeuler/intel-kernel/issues/I73H0T
CVE: NA

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=62faca1ca10cc84e99ae7f38aa28df2bc945369b



Intel-SIG: commit 62faca1c selftests/x86/amx: Add a ptrace test.

--------------------------------

Include a test case to validate the XTILEDATA injection to the target.

Also, it ensures the kernel's ability to copy states between different
XSAVE formats.

Refactor the memcmp() code to be usable for the state validation.

Signed-off-by: default avatarChang S. Bae <chang.seok.bae@intel.com>
Signed-off-by: default avatarDave Hansen <dave.hansen@linux.intel.com>
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/all/20230227210504.18520-3-chang.seok.bae%40intel.com


Signed-off-by: default avatarLin Wang <lin.x.wang@intel.com>
parent e9949bb2
Loading
Loading
Loading
Loading
+105 −3
Original line number Diff line number Diff line
@@ -14,8 +14,10 @@
#include <sys/auxv.h>
#include <sys/mman.h>
#include <sys/shm.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <sys/uio.h>

#include "../kselftest.h" /* For __cpuid_count() */

@@ -583,6 +585,13 @@ static void test_dynamic_state(void)
	_exit(0);
}

static inline int __compare_tiledata_state(struct xsave_buffer *xbuf1, struct xsave_buffer *xbuf2)
{
	return memcmp(&xbuf1->bytes[xtiledata.xbuf_offset],
		      &xbuf2->bytes[xtiledata.xbuf_offset],
		      xtiledata.size);
}

/*
 * Save current register state and compare it to @xbuf1.'
 *
@@ -599,9 +608,7 @@ static inline bool __validate_tiledata_regs(struct xsave_buffer *xbuf1)
		fatal_error("failed to allocate XSAVE buffer\n");

	xsave(xbuf2, XFEATURE_MASK_XTILEDATA);
	ret = memcmp(&xbuf1->bytes[xtiledata.xbuf_offset],
		     &xbuf2->bytes[xtiledata.xbuf_offset],
		     xtiledata.size);
	ret = __compare_tiledata_state(xbuf1, xbuf2);

	free(xbuf2);

@@ -826,6 +833,99 @@ static void test_context_switch(void)
	free(finfo);
}

/* Ptrace test */

/*
 * Make sure the ptracee has the expanded kernel buffer on the first
 * use. Then, initialize the state before performing the state
 * injection from the ptracer.
 */
static inline void ptracee_firstuse_tiledata(void)
{
	load_rand_tiledata(stashed_xsave);
	init_xtiledata();
}

/*
 * Ptracer injects the randomized tile data state. It also reads
 * before and after that, which will execute the kernel's state copy
 * functions. So, the tester is advised to double-check any emitted
 * kernel messages.
 */
static void ptracer_inject_tiledata(pid_t target)
{
	struct xsave_buffer *xbuf;
	struct iovec iov;

	xbuf = alloc_xbuf();
	if (!xbuf)
		fatal_error("unable to allocate XSAVE buffer");

	printf("\tRead the init'ed tiledata via ptrace().\n");

	iov.iov_base = xbuf;
	iov.iov_len = xbuf_size;

	memset(stashed_xsave, 0, xbuf_size);

	if (ptrace(PTRACE_GETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
		fatal_error("PTRACE_GETREGSET");

	if (!__compare_tiledata_state(stashed_xsave, xbuf))
		printf("[OK]\tThe init'ed tiledata was read from ptracee.\n");
	else
		printf("[FAIL]\tThe init'ed tiledata was not read from ptracee.\n");

	printf("\tInject tiledata via ptrace().\n");

	load_rand_tiledata(xbuf);

	memcpy(&stashed_xsave->bytes[xtiledata.xbuf_offset],
	       &xbuf->bytes[xtiledata.xbuf_offset],
	       xtiledata.size);

	if (ptrace(PTRACE_SETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
		fatal_error("PTRACE_SETREGSET");

	if (ptrace(PTRACE_GETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
		fatal_error("PTRACE_GETREGSET");

	if (!__compare_tiledata_state(stashed_xsave, xbuf))
		printf("[OK]\tTiledata was correctly written to ptracee.\n");
	else
		printf("[FAIL]\tTiledata was not correctly written to ptracee.\n");
}

static void test_ptrace(void)
{
	pid_t child;
	int status;

	child = fork();
	if (child < 0) {
		err(1, "fork");
	} else if (!child) {
		if (ptrace(PTRACE_TRACEME, 0, NULL, NULL))
			err(1, "PTRACE_TRACEME");

		ptracee_firstuse_tiledata();

		raise(SIGTRAP);
		_exit(0);
	}

	do {
		wait(&status);
	} while (WSTOPSIG(status) != SIGTRAP);

	ptracer_inject_tiledata(child);

	ptrace(PTRACE_DETACH, child, NULL, NULL);
	wait(&status);
	if (!WIFEXITED(status) || WEXITSTATUS(status))
		err(1, "ptrace test");
}

int main(void)
{
	/* Check hardware availability at first */
@@ -846,6 +946,8 @@ int main(void)
	ctxtswtest_config.num_threads = 5;
	test_context_switch();

	test_ptrace();

	clearhandler(SIGILL);
	free_stashed_xsave();