brintos

brintos / linux-shallow public Read only

0
0
Text · 7.7 KiB · cc3de6f Raw
287 lines · c
1/* SPDX-License-Identifier: GPL-2.0 */2/*3 * mov_ss_trap.c: Exercise the bizarre side effects of a watchpoint on MOV SS4 *5 * This does MOV SS from a watchpointed address followed by various6 * types of kernel entries.  A MOV SS that hits a watchpoint will queue7 * up a #DB trap but will not actually deliver that trap.  The trap8 * will be delivered after the next instruction instead.  The CPU's logic9 * seems to be:10 *11 *  - Any fault: drop the pending #DB trap.12 *  - INT $N, INT3, INTO, SYSCALL, SYSENTER: enter the kernel and then13 *    deliver #DB.14 *  - ICEBP: enter the kernel but do not deliver the watchpoint trap15 *  - breakpoint: only one #DB is delivered (phew!)16 *17 * There are plenty of ways for a kernel to handle this incorrectly.  This18 * test tries to exercise all the cases.19 *20 * This should mostly cover CVE-2018-1087 and CVE-2018-8897.21 */22#define _GNU_SOURCE23 24#include <stdlib.h>25#include <sys/ptrace.h>26#include <sys/types.h>27#include <sys/wait.h>28#include <sys/user.h>29#include <sys/syscall.h>30#include <unistd.h>31#include <errno.h>32#include <stddef.h>33#include <stdio.h>34#include <err.h>35#include <string.h>36#include <setjmp.h>37#include <sys/prctl.h>38 39#define X86_EFLAGS_RF (1UL << 16)40 41#if __x86_64__42# define REG_IP REG_RIP43#else44# define REG_IP REG_EIP45#endif46 47unsigned short ss;48extern unsigned char breakpoint_insn[];49sigjmp_buf jmpbuf;50 51static void enable_watchpoint(void)52{53	pid_t parent = getpid();54	int status;55 56	pid_t child = fork();57	if (child < 0)58		err(1, "fork");59 60	if (child) {61		if (waitpid(child, &status, 0) != child)62			err(1, "waitpid for child");63	} else {64		unsigned long dr0, dr1, dr7;65 66		dr0 = (unsigned long)&ss;67		dr1 = (unsigned long)breakpoint_insn;68		dr7 = ((1UL << 1) |	/* G0 */69		       (3UL << 16) |	/* RW0 = read or write */70		       (1UL << 18) |	/* LEN0 = 2 bytes */71		       (1UL << 3));	/* G1, RW1 = insn */72 73		if (ptrace(PTRACE_ATTACH, parent, NULL, NULL) != 0)74			err(1, "PTRACE_ATTACH");75 76		if (waitpid(parent, &status, 0) != parent)77			err(1, "waitpid for child");78 79		if (ptrace(PTRACE_POKEUSER, parent, (void *)offsetof(struct user, u_debugreg[0]), dr0) != 0)80			err(1, "PTRACE_POKEUSER DR0");81 82		if (ptrace(PTRACE_POKEUSER, parent, (void *)offsetof(struct user, u_debugreg[1]), dr1) != 0)83			err(1, "PTRACE_POKEUSER DR1");84 85		if (ptrace(PTRACE_POKEUSER, parent, (void *)offsetof(struct user, u_debugreg[7]), dr7) != 0)86			err(1, "PTRACE_POKEUSER DR7");87 88		printf("\tDR0 = %lx, DR1 = %lx, DR7 = %lx\n", dr0, dr1, dr7);89 90		if (ptrace(PTRACE_DETACH, parent, NULL, NULL) != 0)91			err(1, "PTRACE_DETACH");92 93		exit(0);94	}95}96 97static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),98		       int flags)99{100	struct sigaction sa;101	memset(&sa, 0, sizeof(sa));102	sa.sa_sigaction = handler;103	sa.sa_flags = SA_SIGINFO | flags;104	sigemptyset(&sa.sa_mask);105	if (sigaction(sig, &sa, 0))106		err(1, "sigaction");107}108 109static char const * const signames[] = {110	[SIGSEGV] = "SIGSEGV",111	[SIGBUS] = "SIBGUS",112	[SIGTRAP] = "SIGTRAP",113	[SIGILL] = "SIGILL",114};115 116static void sigtrap(int sig, siginfo_t *si, void *ctx_void)117{118	ucontext_t *ctx = ctx_void;119 120	printf("\tGot SIGTRAP with RIP=%lx, EFLAGS.RF=%d\n",121	       (unsigned long)ctx->uc_mcontext.gregs[REG_IP],122	       !!(ctx->uc_mcontext.gregs[REG_EFL] & X86_EFLAGS_RF));123}124 125static void handle_and_return(int sig, siginfo_t *si, void *ctx_void)126{127	ucontext_t *ctx = ctx_void;128 129	printf("\tGot %s with RIP=%lx\n", signames[sig],130	       (unsigned long)ctx->uc_mcontext.gregs[REG_IP]);131}132 133static void handle_and_longjmp(int sig, siginfo_t *si, void *ctx_void)134{135	ucontext_t *ctx = ctx_void;136 137	printf("\tGot %s with RIP=%lx\n", signames[sig],138	       (unsigned long)ctx->uc_mcontext.gregs[REG_IP]);139 140	siglongjmp(jmpbuf, 1);141}142 143int main()144{145	unsigned long nr;146 147	asm volatile ("mov %%ss, %[ss]" : [ss] "=m" (ss));148	printf("\tSS = 0x%hx, &SS = 0x%p\n", ss, &ss);149 150	if (prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0) == 0)151		printf("\tPR_SET_PTRACER_ANY succeeded\n");152 153	printf("\tSet up a watchpoint\n");154	sethandler(SIGTRAP, sigtrap, 0);155	enable_watchpoint();156 157	printf("[RUN]\tRead from watched memory (should get SIGTRAP)\n");158	asm volatile ("mov %[ss], %[tmp]" : [tmp] "=r" (nr) : [ss] "m" (ss));159 160	printf("[RUN]\tMOV SS; INT3\n");161	asm volatile ("mov %[ss], %%ss; int3" :: [ss] "m" (ss));162 163	printf("[RUN]\tMOV SS; INT 3\n");164	asm volatile ("mov %[ss], %%ss; .byte 0xcd, 0x3" :: [ss] "m" (ss));165 166	printf("[RUN]\tMOV SS; CS CS INT3\n");167	asm volatile ("mov %[ss], %%ss; .byte 0x2e, 0x2e; int3" :: [ss] "m" (ss));168 169	printf("[RUN]\tMOV SS; CSx14 INT3\n");170	asm volatile ("mov %[ss], %%ss; .fill 14,1,0x2e; int3" :: [ss] "m" (ss));171 172	printf("[RUN]\tMOV SS; INT 4\n");173	sethandler(SIGSEGV, handle_and_return, SA_RESETHAND);174	asm volatile ("mov %[ss], %%ss; int $4" :: [ss] "m" (ss));175 176#ifdef __i386__177	printf("[RUN]\tMOV SS; INTO\n");178	sethandler(SIGSEGV, handle_and_return, SA_RESETHAND);179	nr = -1;180	asm volatile ("add $1, %[tmp]; mov %[ss], %%ss; into"181		      : [tmp] "+r" (nr) : [ss] "m" (ss));182#endif183 184	if (sigsetjmp(jmpbuf, 1) == 0) {185		printf("[RUN]\tMOV SS; ICEBP\n");186 187		/* Some emulators (e.g. QEMU TCG) don't emulate ICEBP. */188		sethandler(SIGILL, handle_and_longjmp, SA_RESETHAND);189 190		asm volatile ("mov %[ss], %%ss; .byte 0xf1" :: [ss] "m" (ss));191	}192 193	if (sigsetjmp(jmpbuf, 1) == 0) {194		printf("[RUN]\tMOV SS; CLI\n");195		sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND);196		asm volatile ("mov %[ss], %%ss; cli" :: [ss] "m" (ss));197	}198 199	if (sigsetjmp(jmpbuf, 1) == 0) {200		printf("[RUN]\tMOV SS; #PF\n");201		sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND);202		asm volatile ("mov %[ss], %%ss; mov (-1), %[tmp]"203			      : [tmp] "=r" (nr) : [ss] "m" (ss));204	}205 206	/*207	 * INT $1: if #DB has DPL=3 and there isn't special handling,208	 * then the kernel will die.209	 */210	if (sigsetjmp(jmpbuf, 1) == 0) {211		printf("[RUN]\tMOV SS; INT 1\n");212		sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND);213		asm volatile ("mov %[ss], %%ss; int $1" :: [ss] "m" (ss));214	}215 216#ifdef __x86_64__217	/*218	 * In principle, we should test 32-bit SYSCALL as well, but219	 * the calling convention is so unpredictable that it's220	 * not obviously worth the effort.221	 */222	if (sigsetjmp(jmpbuf, 1) == 0) {223		printf("[RUN]\tMOV SS; SYSCALL\n");224		sethandler(SIGILL, handle_and_longjmp, SA_RESETHAND);225		nr = SYS_getpid;226		/*227		 * Toggle the high bit of RSP to make it noncanonical to228		 * strengthen this test on non-SMAP systems.229		 */230		asm volatile ("btc $63, %%rsp\n\t"231			      "mov %[ss], %%ss; syscall\n\t"232			      "btc $63, %%rsp"233			      : "+a" (nr) : [ss] "m" (ss)234			      : "rcx"235#ifdef __x86_64__236				, "r11"237#endif238			);239	}240#endif241 242	printf("[RUN]\tMOV SS; breakpointed NOP\n");243	asm volatile ("mov %[ss], %%ss; breakpoint_insn: nop" :: [ss] "m" (ss));244 245	/*246	 * Invoking SYSENTER directly breaks all the rules.  Just handle247	 * the SIGSEGV.248	 */249	if (sigsetjmp(jmpbuf, 1) == 0) {250		printf("[RUN]\tMOV SS; SYSENTER\n");251		stack_t stack = {252			.ss_sp = malloc(sizeof(char) * SIGSTKSZ),253			.ss_size = SIGSTKSZ,254		};255		if (sigaltstack(&stack, NULL) != 0)256			err(1, "sigaltstack");257		sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND | SA_ONSTACK);258		nr = SYS_getpid;259		free(stack.ss_sp);260		/* Clear EBP first to make sure we segfault cleanly. */261		asm volatile ("xorl %%ebp, %%ebp; mov %[ss], %%ss; SYSENTER" : "+a" (nr)262			      : [ss] "m" (ss) : "flags", "rcx"263#ifdef __x86_64__264				, "r11"265#endif266			);267 268		/* We're unreachable here.  SYSENTER forgets RIP. */269	}270 271	if (sigsetjmp(jmpbuf, 1) == 0) {272		printf("[RUN]\tMOV SS; INT $0x80\n");273		sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND);274		nr = 20;	/* compat getpid */275		asm volatile ("mov %[ss], %%ss; int $0x80"276			      : "+a" (nr) : [ss] "m" (ss)277			      : "flags"278#ifdef __x86_64__279				, "r8", "r9", "r10", "r11"280#endif281			);282	}283 284	printf("[OK]\tI aten't dead\n");285	return 0;286}287