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