0001
0002
0003
0004
0005
0006
0007 #define _GNU_SOURCE
0008
0009 #include <stdlib.h>
0010 #include <unistd.h>
0011 #include <stdio.h>
0012 #include <string.h>
0013 #include <inttypes.h>
0014 #include <sys/signal.h>
0015 #include <sys/ucontext.h>
0016 #include <sys/syscall.h>
0017 #include <err.h>
0018 #include <stddef.h>
0019 #include <stdbool.h>
0020 #include <setjmp.h>
0021 #include <sys/user.h>
0022 #include <sys/mman.h>
0023 #include <assert.h>
0024
0025
0026 asm (
0027 ".pushsection \".text\", \"ax\"\n\t"
0028 ".balign 4096\n\t"
0029 "test_page: .globl test_page\n\t"
0030 ".fill 4094,1,0xcc\n\t"
0031 "test_syscall_insn:\n\t"
0032 "syscall\n\t"
0033 ".ifne . - test_page - 4096\n\t"
0034 ".error \"test page is not one page long\"\n\t"
0035 ".endif\n\t"
0036 ".popsection"
0037 );
0038
0039 extern const char test_page[];
0040 static void const *current_test_page_addr = test_page;
0041
0042 static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
0043 int flags)
0044 {
0045 struct sigaction sa;
0046 memset(&sa, 0, sizeof(sa));
0047 sa.sa_sigaction = handler;
0048 sa.sa_flags = SA_SIGINFO | flags;
0049 sigemptyset(&sa.sa_mask);
0050 if (sigaction(sig, &sa, 0))
0051 err(1, "sigaction");
0052 }
0053
0054 static void clearhandler(int sig)
0055 {
0056 struct sigaction sa;
0057 memset(&sa, 0, sizeof(sa));
0058 sa.sa_handler = SIG_DFL;
0059 sigemptyset(&sa.sa_mask);
0060 if (sigaction(sig, &sa, 0))
0061 err(1, "sigaction");
0062 }
0063
0064
0065 static gregset_t initial_regs;
0066
0067 static volatile unsigned long rip;
0068
0069 static void sigsegv_for_sigreturn_test(int sig, siginfo_t *info, void *ctx_void)
0070 {
0071 ucontext_t *ctx = (ucontext_t*)ctx_void;
0072
0073 if (rip != ctx->uc_mcontext.gregs[REG_RIP]) {
0074 printf("[FAIL]\tRequested RIP=0x%lx but got RIP=0x%lx\n",
0075 rip, (unsigned long)ctx->uc_mcontext.gregs[REG_RIP]);
0076 fflush(stdout);
0077 _exit(1);
0078 }
0079
0080 memcpy(&ctx->uc_mcontext.gregs, &initial_regs, sizeof(gregset_t));
0081
0082 printf("[OK]\tGot SIGSEGV at RIP=0x%lx\n", rip);
0083 }
0084
0085 static void sigusr1(int sig, siginfo_t *info, void *ctx_void)
0086 {
0087 ucontext_t *ctx = (ucontext_t*)ctx_void;
0088
0089 memcpy(&initial_regs, &ctx->uc_mcontext.gregs, sizeof(gregset_t));
0090
0091
0092 ctx->uc_mcontext.gregs[REG_RIP] = rip;
0093 ctx->uc_mcontext.gregs[REG_RCX] = rip;
0094
0095
0096 assert(ctx->uc_mcontext.gregs[REG_EFL] ==
0097 ctx->uc_mcontext.gregs[REG_R11]);
0098
0099 sethandler(SIGSEGV, sigsegv_for_sigreturn_test, SA_RESETHAND);
0100
0101 return;
0102 }
0103
0104 static void test_sigreturn_to(unsigned long ip)
0105 {
0106 rip = ip;
0107 printf("[RUN]\tsigreturn to 0x%lx\n", ip);
0108 raise(SIGUSR1);
0109 }
0110
0111 static jmp_buf jmpbuf;
0112
0113 static void sigsegv_for_fallthrough(int sig, siginfo_t *info, void *ctx_void)
0114 {
0115 ucontext_t *ctx = (ucontext_t*)ctx_void;
0116
0117 if (rip != ctx->uc_mcontext.gregs[REG_RIP]) {
0118 printf("[FAIL]\tExpected SIGSEGV at 0x%lx but got RIP=0x%lx\n",
0119 rip, (unsigned long)ctx->uc_mcontext.gregs[REG_RIP]);
0120 fflush(stdout);
0121 _exit(1);
0122 }
0123
0124 siglongjmp(jmpbuf, 1);
0125 }
0126
0127 static void test_syscall_fallthrough_to(unsigned long ip)
0128 {
0129 void *new_address = (void *)(ip - 4096);
0130 void *ret;
0131
0132 printf("[RUN]\tTrying a SYSCALL that falls through to 0x%lx\n", ip);
0133
0134 ret = mremap((void *)current_test_page_addr, 4096, 4096,
0135 MREMAP_MAYMOVE | MREMAP_FIXED, new_address);
0136 if (ret == MAP_FAILED) {
0137 if (ip <= (1UL << 47) - PAGE_SIZE) {
0138 err(1, "mremap to %p", new_address);
0139 } else {
0140 printf("[OK]\tmremap to %p failed\n", new_address);
0141 return;
0142 }
0143 }
0144
0145 if (ret != new_address)
0146 errx(1, "mremap malfunctioned: asked for %p but got %p\n",
0147 new_address, ret);
0148
0149 current_test_page_addr = new_address;
0150 rip = ip;
0151
0152 if (sigsetjmp(jmpbuf, 1) == 0) {
0153 asm volatile ("call *%[syscall_insn]" :: "a" (SYS_getpid),
0154 [syscall_insn] "rm" (ip - 2));
0155 errx(1, "[FAIL]\tSyscall trampoline returned");
0156 }
0157
0158 printf("[OK]\tWe survived\n");
0159 }
0160
0161 int main()
0162 {
0163
0164
0165
0166
0167
0168
0169 sethandler(SIGUSR1, sigusr1, 0);
0170 for (int i = 47; i < 64; i++)
0171 test_sigreturn_to(1UL<<i);
0172
0173 clearhandler(SIGUSR1);
0174
0175 sethandler(SIGSEGV, sigsegv_for_fallthrough, 0);
0176
0177
0178 test_syscall_fallthrough_to((1UL << 47) - 2*PAGE_SIZE);
0179
0180
0181 for (int i = 47; i < 64; i++) {
0182 test_syscall_fallthrough_to((1UL<<i) - PAGE_SIZE);
0183 test_syscall_fallthrough_to(1UL<<i);
0184 }
0185
0186 return 0;
0187 }