/* $Id: sysret.c,v 1.6 2012/06/18 17:49:05 kostik Exp kostik $ */ #include #include #include #include #include #include #include #include #include #include /* * Location of the shared page on amd64. Also, the last valid usermode * page. Overflow of %rip from the last byte causes sysretq to try * load non-canonical %rip. */ static const unsigned long last_page = 0x800000000000 - PAGE_SIZE; static const char sc[] = { 0x48, 0xc7, 0xc0, SYS_fork, 0x00, 0x00, 0x00, /* mov $syscall_no,%rax */ 0x49, 0x89, 0xca, /* mov %rcx,%r10 */ 0x0f, 0x05 /* syscall */ }; static void sigbus_handler(int signo, siginfo_t *si, void *v) { ucontext_t *uc; mcontext_t *mc; uc = v; mc = &uc->uc_mcontext; printf("sig %d si_addr %p rip %lx rcx %lx\n", signo, si->si_addr, mc->mc_rip, mc->mc_rcx); exit(0); } int main(void) { char *p, *code, *saved; struct sigaction sa; saved = malloc(PAGE_SIZE); if (saved == NULL) err(1, "malloc"); memcpy(saved, (const char *)last_page, PAGE_SIZE); memset(&sa, 0, sizeof(sa)); sa.sa_flags = SA_SIGINFO; sa.sa_sigaction = sigbus_handler; if (sigaction(SIGBUS, &sa, NULL) == -1) err(1, "sigaction(SIGBUS"); if (sigaction(SIGSEGV, &sa, NULL) == -1) err(1, "sigaction(SIGSEGV"); p = mmap((char *)last_page, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0); if (p == (char *)MAP_FAILED) err(1, "mmap"); /* * Restore the signal trampoline. This relies on the kernel * allocating chunks from shared pages starting from the lower * addresses. */ memcpy(p, saved, PAGE_SIZE); code = p + PAGE_SIZE - sizeof(sc); memcpy(code, sc, sizeof(sc)); __asm __volatile("jmp *%0" : : "r" (code) : "memory"); return (0); }