cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

mov_ss_trap.c (7843B)


      1/* SPDX-License-Identifier: GPL-2.0 */
      2/*
      3 * mov_ss_trap.c: Exercise the bizarre side effects of a watchpoint on MOV SS
      4 *
      5 * This does MOV SS from a watchpointed address followed by various
      6 * types of kernel entries.  A MOV SS that hits a watchpoint will queue
      7 * up a #DB trap but will not actually deliver that trap.  The trap
      8 * will be delivered after the next instruction instead.  The CPU's logic
      9 * seems to be:
     10 *
     11 *  - Any fault: drop the pending #DB trap.
     12 *  - INT $N, INT3, INTO, SYSCALL, SYSENTER: enter the kernel and then
     13 *    deliver #DB.
     14 *  - ICEBP: enter the kernel but do not deliver the watchpoint trap
     15 *  - breakpoint: only one #DB is delivered (phew!)
     16 *
     17 * There are plenty of ways for a kernel to handle this incorrectly.  This
     18 * test tries to exercise all the cases.
     19 *
     20 * This should mostly cover CVE-2018-1087 and CVE-2018-8897.
     21 */
     22#define _GNU_SOURCE
     23
     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_RIP
     43#else
     44# define REG_IP REG_EIP
     45#endif
     46
     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#endif
    183
    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, but
    219	 * the calling convention is so unpredictable that it's
    220	 * 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 to
    228		 * 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#endif
    238			);
    239	}
    240#endif
    241
    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 handle
    247	 * 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#endif
    266			);
    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#endif
    281			);
    282	}
    283
    284	printf("[OK]\tI aten't dead\n");
    285	return 0;
    286}