1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright (c) 2017 Google, Inc.
4f08c3bdfSopenharmony_ci */
5f08c3bdfSopenharmony_ci
6f08c3bdfSopenharmony_ci/*
7f08c3bdfSopenharmony_ci * Regression test for commit 814fb7bb7db5 ("x86/fpu: Don't let userspace set
8f08c3bdfSopenharmony_ci * bogus xcomp_bv"), or CVE-2017-15537.  This bug allowed ptrace(pid,
9f08c3bdfSopenharmony_ci * PTRACE_SETREGSET, NT_X86_XSTATE, &iov) to assign a task an invalid FPU state
10f08c3bdfSopenharmony_ci * --- specifically, by setting reserved bits in xstate_header.xcomp_bv.  This
11f08c3bdfSopenharmony_ci * made restoring the FPU registers fail when switching to the task, causing the
12f08c3bdfSopenharmony_ci * FPU registers to take on the values from other tasks.
13f08c3bdfSopenharmony_ci *
14f08c3bdfSopenharmony_ci * To detect the bug, we have a subprocess run a loop checking its xmm0 register
15f08c3bdfSopenharmony_ci * for corruption.  This detects the case where the FPU state became invalid and
16f08c3bdfSopenharmony_ci * the kernel is not restoring the process's registers.  Note that we have to
17f08c3bdfSopenharmony_ci * set the expected value of xmm0 to all 0's since it is acceptable behavior for
18f08c3bdfSopenharmony_ci * the kernel to simply reinitialize the FPU state upon seeing that it is
19f08c3bdfSopenharmony_ci * invalid.  To increase the chance of detecting the problem, we also create
20f08c3bdfSopenharmony_ci * additional subprocesses that spin with different xmm0 contents.
21f08c3bdfSopenharmony_ci *
22f08c3bdfSopenharmony_ci * Thus bug affected the x86 architecture only.  Other architectures could have
23f08c3bdfSopenharmony_ci * similar bugs as well, but this test has to be x86-specific because it has to
24f08c3bdfSopenharmony_ci * know about the architecture-dependent FPU state.
25f08c3bdfSopenharmony_ci */
26f08c3bdfSopenharmony_ci
27f08c3bdfSopenharmony_ci#include "tst_test.h"
28f08c3bdfSopenharmony_ci
29f08c3bdfSopenharmony_ci#ifdef __x86_64__
30f08c3bdfSopenharmony_ci
31f08c3bdfSopenharmony_ci#include <errno.h>
32f08c3bdfSopenharmony_ci#include <inttypes.h>
33f08c3bdfSopenharmony_ci#include <sched.h>
34f08c3bdfSopenharmony_ci#include <stdbool.h>
35f08c3bdfSopenharmony_ci#include <stdlib.h>
36f08c3bdfSopenharmony_ci#include <sys/uio.h>
37f08c3bdfSopenharmony_ci#include <sys/wait.h>
38f08c3bdfSopenharmony_ci
39f08c3bdfSopenharmony_ci#include "config.h"
40f08c3bdfSopenharmony_ci#include "ptrace.h"
41f08c3bdfSopenharmony_ci#include "tst_safe_macros.h"
42f08c3bdfSopenharmony_ci#include "lapi/cpuid.h"
43f08c3bdfSopenharmony_ci
44f08c3bdfSopenharmony_ci#ifndef PTRACE_GETREGSET
45f08c3bdfSopenharmony_ci# define PTRACE_GETREGSET 0x4204
46f08c3bdfSopenharmony_ci#endif
47f08c3bdfSopenharmony_ci
48f08c3bdfSopenharmony_ci#ifndef PTRACE_SETREGSET
49f08c3bdfSopenharmony_ci# define PTRACE_SETREGSET 0x4205
50f08c3bdfSopenharmony_ci#endif
51f08c3bdfSopenharmony_ci
52f08c3bdfSopenharmony_ci#ifndef NT_X86_XSTATE
53f08c3bdfSopenharmony_ci# define NT_X86_XSTATE 0x202
54f08c3bdfSopenharmony_ci#endif
55f08c3bdfSopenharmony_ci
56f08c3bdfSopenharmony_ci#ifndef CPUID_LEAF_XSTATE
57f08c3bdfSopenharmony_ci# define CPUID_LEAF_XSTATE 0xd
58f08c3bdfSopenharmony_ci#endif
59f08c3bdfSopenharmony_ci
60f08c3bdfSopenharmony_cistatic void check_regs_loop(uint32_t initval)
61f08c3bdfSopenharmony_ci{
62f08c3bdfSopenharmony_ci	const unsigned long num_iters = 1000000000;
63f08c3bdfSopenharmony_ci	uint32_t xmm0[4] = { initval, initval, initval, initval };
64f08c3bdfSopenharmony_ci	int status = 1;
65f08c3bdfSopenharmony_ci
66f08c3bdfSopenharmony_ci	asm volatile("   movdqu %0, %%xmm0\n"
67f08c3bdfSopenharmony_ci		     "   mov %0, %%rbx\n"
68f08c3bdfSopenharmony_ci		     "1: dec %2\n"
69f08c3bdfSopenharmony_ci		     "   jz 2f\n"
70f08c3bdfSopenharmony_ci		     "   movdqu %%xmm0, %0\n"
71f08c3bdfSopenharmony_ci		     "   mov %0, %%rax\n"
72f08c3bdfSopenharmony_ci		     "   cmp %%rax, %%rbx\n"
73f08c3bdfSopenharmony_ci		     "   je 1b\n"
74f08c3bdfSopenharmony_ci		     "   jmp 3f\n"
75f08c3bdfSopenharmony_ci		     "2: mov $0, %1\n"
76f08c3bdfSopenharmony_ci		     "3:\n"
77f08c3bdfSopenharmony_ci		     : "+m" (xmm0), "+r" (status)
78f08c3bdfSopenharmony_ci		     : "r" (num_iters) : "rax", "rbx", "xmm0");
79f08c3bdfSopenharmony_ci
80f08c3bdfSopenharmony_ci	if (status) {
81f08c3bdfSopenharmony_ci		tst_res(TFAIL,
82f08c3bdfSopenharmony_ci			"xmm registers corrupted!  initval=%08X, xmm0=%08X%08X%08X%08X\n",
83f08c3bdfSopenharmony_ci			initval, xmm0[0], xmm0[1], xmm0[2], xmm0[3]);
84f08c3bdfSopenharmony_ci	}
85f08c3bdfSopenharmony_ci	exit(status);
86f08c3bdfSopenharmony_ci}
87f08c3bdfSopenharmony_ci
88f08c3bdfSopenharmony_cistatic void do_test(void)
89f08c3bdfSopenharmony_ci{
90f08c3bdfSopenharmony_ci	int i;
91f08c3bdfSopenharmony_ci	int num_cpus = tst_ncpus();
92f08c3bdfSopenharmony_ci	pid_t pid;
93f08c3bdfSopenharmony_ci	uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
94f08c3bdfSopenharmony_ci	uint64_t *xstate;
95f08c3bdfSopenharmony_ci	/*
96f08c3bdfSopenharmony_ci	 * CPUID.(EAX=0DH, ECX=0H):EBX: maximum size (bytes, from the beginning
97f08c3bdfSopenharmony_ci	 * of the XSAVE/XRSTOR save area) required by enabled features in XCR0.
98f08c3bdfSopenharmony_ci	 */
99f08c3bdfSopenharmony_ci	__cpuid_count(CPUID_LEAF_XSTATE, ecx, eax, ebx, ecx, edx);
100f08c3bdfSopenharmony_ci	xstate = SAFE_MEMALIGN(64, ebx);
101f08c3bdfSopenharmony_ci	struct iovec iov = { .iov_base = xstate, .iov_len = ebx };
102f08c3bdfSopenharmony_ci	int status;
103f08c3bdfSopenharmony_ci	bool okay;
104f08c3bdfSopenharmony_ci
105f08c3bdfSopenharmony_ci	tst_res(TINFO, "CPUID.(EAX=%u, ECX=0):EAX=%u, EBX=%u, ECX=%u, EDX=%u",
106f08c3bdfSopenharmony_ci		CPUID_LEAF_XSTATE, eax, ebx, ecx, edx);
107f08c3bdfSopenharmony_ci	pid = SAFE_FORK();
108f08c3bdfSopenharmony_ci	if (pid == 0) {
109f08c3bdfSopenharmony_ci		TST_CHECKPOINT_WAKE(0);
110f08c3bdfSopenharmony_ci		check_regs_loop(0x00000000);
111f08c3bdfSopenharmony_ci	}
112f08c3bdfSopenharmony_ci	for (i = 0; i < num_cpus; i++) {
113f08c3bdfSopenharmony_ci		if (SAFE_FORK() == 0)
114f08c3bdfSopenharmony_ci			check_regs_loop(0xDEADBEEF);
115f08c3bdfSopenharmony_ci	}
116f08c3bdfSopenharmony_ci
117f08c3bdfSopenharmony_ci	TST_CHECKPOINT_WAIT(0);
118f08c3bdfSopenharmony_ci	sched_yield();
119f08c3bdfSopenharmony_ci
120f08c3bdfSopenharmony_ci	TEST(ptrace(PTRACE_ATTACH, pid, 0, 0));
121f08c3bdfSopenharmony_ci	if (TST_RET != 0) {
122f08c3bdfSopenharmony_ci		free(xstate);
123f08c3bdfSopenharmony_ci		tst_brk(TBROK | TTERRNO, "PTRACE_ATTACH failed");
124f08c3bdfSopenharmony_ci	}
125f08c3bdfSopenharmony_ci
126f08c3bdfSopenharmony_ci	SAFE_WAITPID(pid, NULL, 0);
127f08c3bdfSopenharmony_ci	TEST(ptrace(PTRACE_GETREGSET, pid, NT_X86_XSTATE, &iov));
128f08c3bdfSopenharmony_ci	if (TST_RET != 0) {
129f08c3bdfSopenharmony_ci		free(xstate);
130f08c3bdfSopenharmony_ci		if (TST_ERR == EIO)
131f08c3bdfSopenharmony_ci			tst_brk(TCONF, "GETREGSET/SETREGSET is unsupported");
132f08c3bdfSopenharmony_ci
133f08c3bdfSopenharmony_ci		if (TST_ERR == EINVAL)
134f08c3bdfSopenharmony_ci			tst_brk(TCONF, "NT_X86_XSTATE is unsupported");
135f08c3bdfSopenharmony_ci
136f08c3bdfSopenharmony_ci		if (TST_ERR == ENODEV)
137f08c3bdfSopenharmony_ci			tst_brk(TCONF, "CPU doesn't support XSAVE instruction");
138f08c3bdfSopenharmony_ci
139f08c3bdfSopenharmony_ci		tst_brk(TBROK | TTERRNO,
140f08c3bdfSopenharmony_ci			"PTRACE_GETREGSET failed with unexpected error");
141f08c3bdfSopenharmony_ci	}
142f08c3bdfSopenharmony_ci
143f08c3bdfSopenharmony_ci	xstate[65] = -1; /* sets all bits in xstate_header.xcomp_bv */
144f08c3bdfSopenharmony_ci
145f08c3bdfSopenharmony_ci	/*
146f08c3bdfSopenharmony_ci	 * Old kernels simply masked out all the reserved bits in the xstate
147f08c3bdfSopenharmony_ci	 * header (causing the PTRACE_SETREGSET command here to succeed), while
148f08c3bdfSopenharmony_ci	 * new kernels will reject them (causing the PTRACE_SETREGSET command
149f08c3bdfSopenharmony_ci	 * here to fail with EINVAL).  We accept either behavior, as neither
150f08c3bdfSopenharmony_ci	 * behavior reliably tells us whether the real bug (which we test for
151f08c3bdfSopenharmony_ci	 * below in either case) is present.
152f08c3bdfSopenharmony_ci	 */
153f08c3bdfSopenharmony_ci	TEST(ptrace(PTRACE_SETREGSET, pid, NT_X86_XSTATE, &iov));
154f08c3bdfSopenharmony_ci	if (TST_RET == 0) {
155f08c3bdfSopenharmony_ci		tst_res(TINFO, "PTRACE_SETREGSET with reserved bits succeeded");
156f08c3bdfSopenharmony_ci	} else if (TST_ERR == EINVAL) {
157f08c3bdfSopenharmony_ci		tst_res(TINFO,
158f08c3bdfSopenharmony_ci			"PTRACE_SETREGSET with reserved bits failed with EINVAL");
159f08c3bdfSopenharmony_ci	} else {
160f08c3bdfSopenharmony_ci		free(xstate);
161f08c3bdfSopenharmony_ci		tst_brk(TBROK | TTERRNO,
162f08c3bdfSopenharmony_ci			"PTRACE_SETREGSET failed with unexpected error");
163f08c3bdfSopenharmony_ci	}
164f08c3bdfSopenharmony_ci
165f08c3bdfSopenharmony_ci	/*
166f08c3bdfSopenharmony_ci	 * It is possible for test child 'pid' to crash on AMD
167f08c3bdfSopenharmony_ci	 * systems (e.g. AMD Opteron(TM) Processor 6234) with
168f08c3bdfSopenharmony_ci	 * older kernels. This causes tracee to stop and sleep
169f08c3bdfSopenharmony_ci	 * in ptrace_stop(). Without resuming the tracee, the
170f08c3bdfSopenharmony_ci	 * test hangs at do_test()->tst_reap_children() called
171f08c3bdfSopenharmony_ci	 * by the library. Use detach here, so we don't need to
172f08c3bdfSopenharmony_ci	 * worry about potential stops after this point.
173f08c3bdfSopenharmony_ci	 */
174f08c3bdfSopenharmony_ci	TEST(ptrace(PTRACE_DETACH, pid, 0, 0));
175f08c3bdfSopenharmony_ci	if (TST_RET != 0) {
176f08c3bdfSopenharmony_ci		free(xstate);
177f08c3bdfSopenharmony_ci		tst_brk(TBROK | TTERRNO, "PTRACE_DETACH failed");
178f08c3bdfSopenharmony_ci	}
179f08c3bdfSopenharmony_ci
180f08c3bdfSopenharmony_ci	/* If child 'pid' crashes, only report it as info. */
181f08c3bdfSopenharmony_ci	SAFE_WAITPID(pid, &status, 0);
182f08c3bdfSopenharmony_ci	if (WIFEXITED(status)) {
183f08c3bdfSopenharmony_ci		tst_res(TINFO, "test child %d exited, retcode: %d",
184f08c3bdfSopenharmony_ci			pid, WEXITSTATUS(status));
185f08c3bdfSopenharmony_ci	}
186f08c3bdfSopenharmony_ci	if (WIFSIGNALED(status)) {
187f08c3bdfSopenharmony_ci		tst_res(TINFO, "test child %d exited, termsig: %d",
188f08c3bdfSopenharmony_ci			pid, WTERMSIG(status));
189f08c3bdfSopenharmony_ci	}
190f08c3bdfSopenharmony_ci
191f08c3bdfSopenharmony_ci	okay = true;
192f08c3bdfSopenharmony_ci	for (i = 0; i < num_cpus; i++) {
193f08c3bdfSopenharmony_ci		SAFE_WAIT(&status);
194f08c3bdfSopenharmony_ci		okay &= (WIFEXITED(status) && WEXITSTATUS(status) == 0);
195f08c3bdfSopenharmony_ci	}
196f08c3bdfSopenharmony_ci	if (okay)
197f08c3bdfSopenharmony_ci		tst_res(TPASS, "wasn't able to set invalid FPU state");
198f08c3bdfSopenharmony_ci	free(xstate);
199f08c3bdfSopenharmony_ci}
200f08c3bdfSopenharmony_ci
201f08c3bdfSopenharmony_cistatic struct tst_test test = {
202f08c3bdfSopenharmony_ci	.test_all = do_test,
203f08c3bdfSopenharmony_ci	.forks_child = 1,
204f08c3bdfSopenharmony_ci	.needs_checkpoints = 1,
205f08c3bdfSopenharmony_ci	.supported_archs = (const char *const []) {
206f08c3bdfSopenharmony_ci		"x86_64",
207f08c3bdfSopenharmony_ci		NULL
208f08c3bdfSopenharmony_ci	},
209f08c3bdfSopenharmony_ci	.tags = (const struct tst_tag[]) {
210f08c3bdfSopenharmony_ci		{"linux-git", "814fb7bb7db5"},
211f08c3bdfSopenharmony_ci		{"CVE", "2017-15537"},
212f08c3bdfSopenharmony_ci		{}
213f08c3bdfSopenharmony_ci	}
214f08c3bdfSopenharmony_ci
215f08c3bdfSopenharmony_ci};
216f08c3bdfSopenharmony_ci
217f08c3bdfSopenharmony_ci#else
218f08c3bdfSopenharmony_ciTST_TEST_TCONF("Tests an x86_64 feature");
219f08c3bdfSopenharmony_ci#endif	/* if x86 */
220