Commit 833c12ce authored by Weihong Zhang's avatar Weihong Zhang Committed by Dave Hansen
Browse files

selftests/x86/lam: Add inherit test cases for linear-address masking



LAM is enabled per-thread and gets inherited on fork(2)/clone(2). exec()
reverts LAM status to the default disabled state.

There are two test scenarios:

 - Fork test cases:

   These cases were used to test the inheritance of LAM for per-thread,
   Child process generated by fork() should inherit LAM feature from
   parent process, Child process can get the LAM mode same as parent
   process.

 - Execve test cases:

   Processes generated by execve() are different from processes
   generated by fork(), these processes revert LAM status to disabled
   status.

Signed-off-by: default avatarWeihong Zhang <weihong.zhang@intel.com>
Signed-off-by: default avatarKirill A. Shutemov <kirill.shutemov@linux.intel.com>
Signed-off-by: default avatarDave Hansen <dave.hansen@linux.intel.com>
Acked-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/all/20230312112612.31869-16-kirill.shutemov%40linux.intel.com
parent 72fd6d73
Loading
Loading
Loading
Loading
+121 −4
Original line number Diff line number Diff line
@@ -37,8 +37,9 @@
#define FUNC_MMAP               0x4
#define FUNC_SYSCALL            0x8
#define FUNC_URING              0x10
#define FUNC_INHERITE           0x20

#define TEST_MASK               0x1f
#define TEST_MASK               0x3f

#define LOW_ADDR                (0x1UL << 30)
#define HIGH_ADDR               (0x3UL << 48)
@@ -174,6 +175,28 @@ static unsigned long get_default_tag_bits(void)
	return lam;
}

/*
 * Set tagged address and read back untag mask.
 * check if the untag mask is expected.
 */
static int get_lam(void)
{
	uint64_t ptr = 0;
	int ret = -1;
	/* Get untagged mask */
	if (syscall(SYS_arch_prctl, ARCH_GET_UNTAG_MASK, &ptr) == -1)
		return -1;

	/* Check mask returned is expected */
	if (ptr == ~(LAM_U57_MASK))
		ret = LAM_U57_BITS;
	else if (ptr == -1ULL)
		ret = LAM_NONE;


	return ret;
}

/* According to LAM mode, set metadata in high bits */
static uint64_t set_metadata(uint64_t src, unsigned long lam)
{
@@ -581,7 +604,7 @@ int do_uring(unsigned long lam)

			switch (lam) {
			case LAM_U57_BITS: /* Clear bits 62:57 */
				addr = (addr & ~(0x3fULL << 57));
				addr = (addr & ~(LAM_U57_MASK));
				break;
			}
			free((void *)addr);
@@ -632,6 +655,72 @@ static int fork_test(struct testcases *test)
	return ret;
}

static int handle_execve(struct testcases *test)
{
	int ret, child_ret;
	int lam = test->lam;
	pid_t pid;

	pid = fork();
	if (pid < 0) {
		perror("Fork failed.");
		ret = 1;
	} else if (pid == 0) {
		char path[PATH_MAX];

		/* Set LAM mode in parent process */
		if (set_lam(lam) != 0)
			return 1;

		/* Get current binary's path and the binary was run by execve */
		if (readlink("/proc/self/exe", path, PATH_MAX) <= 0)
			exit(-1);

		/* run binary to get LAM mode and return to parent process */
		if (execlp(path, path, "-t 0x0", NULL) < 0) {
			perror("error on exec");
			exit(-1);
		}
	} else {
		wait(&child_ret);
		ret = WEXITSTATUS(child_ret);
		if (ret != LAM_NONE)
			return 1;
	}

	return 0;
}

static int handle_inheritance(struct testcases *test)
{
	int ret, child_ret;
	int lam = test->lam;
	pid_t pid;

	/* Set LAM mode in parent process */
	if (set_lam(lam) != 0)
		return 1;

	pid = fork();
	if (pid < 0) {
		perror("Fork failed.");
		return 1;
	} else if (pid == 0) {
		/* Set LAM mode in parent process */
		int child_lam = get_lam();

		exit(child_lam);
	} else {
		wait(&child_ret);
		ret = WEXITSTATUS(child_ret);

		if (lam != ret)
			return 1;
	}

	return 0;
}

static void run_test(struct testcases *test, int count)
{
	int i, ret = 0;
@@ -740,11 +829,26 @@ static struct testcases mmap_cases[] = {
	},
};

static struct testcases inheritance_cases[] = {
	{
		.expected = 0,
		.lam = LAM_U57_BITS,
		.test_func = handle_inheritance,
		.msg = "FORK: LAM_U57, child process should get LAM mode same as parent\n",
	},
	{
		.expected = 0,
		.lam = LAM_U57_BITS,
		.test_func = handle_execve,
		.msg = "EXECVE: LAM_U57, child process should get disabled LAM mode\n",
	},
};

static void cmd_help(void)
{
	printf("usage: lam [-h] [-t test list]\n");
	printf("\t-t test list: run tests specified in the test list, default:0x%x\n", TEST_MASK);
	printf("\t\t0x1:malloc; 0x2:max_bits; 0x4:mmap; 0x8:syscall; 0x10:io_uring.\n");
	printf("\t\t0x1:malloc; 0x2:max_bits; 0x4:mmap; 0x8:syscall; 0x10:io_uring; 0x20:inherit;\n");
	printf("\t-h: help\n");
}

@@ -764,7 +868,7 @@ int main(int argc, char **argv)
		switch (c) {
		case 't':
			tests = strtoul(optarg, NULL, 16);
			if (!(tests & TEST_MASK)) {
			if (tests && !(tests & TEST_MASK)) {
				ksft_print_msg("Invalid argument!\n");
				return -1;
			}
@@ -778,6 +882,16 @@ int main(int argc, char **argv)
		}
	}

	/*
	 * When tests is 0, it is not a real test case;
	 * the option used by test case(execve) to check the lam mode in
	 * process generated by execve, the process read back lam mode and
	 * check with lam mode in parent process.
	 */
	if (!tests)
		return (get_lam());

	/* Run test cases */
	if (tests & FUNC_MALLOC)
		run_test(malloc_cases, ARRAY_SIZE(malloc_cases));

@@ -793,6 +907,9 @@ int main(int argc, char **argv)
	if (tests & FUNC_URING)
		run_test(uring_cases, ARRAY_SIZE(uring_cases));

	if (tests & FUNC_INHERITE)
		run_test(inheritance_cases, ARRAY_SIZE(inheritance_cases));

	ksft_set_plan(tests_cnt);

	return ksft_exit_pass();