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