1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2021 Benjamin Berg <benjamin@sipsolutions.net> 4 */ 5 6 #include <sysdep/stub.h> 7 8 #include <linux/futex.h> 9 #include <errno.h> 10 11 static __always_inline int syscall_handler(struct stub_data *d) 12 { 13 int i; 14 unsigned long res; 15 16 for (i = 0; i < d->syscall_data_len; i++) { 17 struct stub_syscall *sc = &d->syscall_data[i]; 18 19 switch (sc->syscall) { 20 case STUB_SYSCALL_MMAP: 21 res = stub_syscall6(STUB_MMAP_NR, 22 sc->mem.addr, sc->mem.length, 23 sc->mem.prot, 24 MAP_SHARED | MAP_FIXED, 25 sc->mem.fd, sc->mem.offset); 26 if (res != sc->mem.addr) { 27 d->err = res; 28 d->syscall_data_len = i; 29 return -1; 30 } 31 break; 32 case STUB_SYSCALL_MUNMAP: 33 res = stub_syscall2(__NR_munmap, 34 sc->mem.addr, sc->mem.length); 35 if (res) { 36 d->err = res; 37 d->syscall_data_len = i; 38 return -1; 39 } 40 break; 41 default: 42 d->err = -95; /* EOPNOTSUPP */ 43 d->syscall_data_len = i; 44 return -1; 45 } 46 } 47 48 d->err = 0; 49 d->syscall_data_len = 0; 50 51 return 0; 52 } 53 54 void __section(".__syscall_stub") 55 stub_syscall_handler(void) 56 { 57 struct stub_data *d = get_stub_data(); 58 59 syscall_handler(d); 60 61 trap_myself(); 62 } 63 64 void __section(".__syscall_stub") 65 stub_signal_interrupt(int sig, siginfo_t *info, void *p) 66 { 67 struct stub_data *d = get_stub_data(); 68 ucontext_t *uc = p; 69 long res; 70 71 d->signal = sig; 72 d->si_offset = (unsigned long)info - (unsigned long)&d->sigstack[0]; 73 d->mctx_offset = (unsigned long)&uc->uc_mcontext - (unsigned long)&d->sigstack[0]; 74 75 restart_wait: 76 d->futex = FUTEX_IN_KERN; 77 do { 78 res = stub_syscall3(__NR_futex, (unsigned long)&d->futex, 79 FUTEX_WAKE, 1); 80 } while (res == -EINTR); 81 do { 82 res = stub_syscall4(__NR_futex, (unsigned long)&d->futex, 83 FUTEX_WAIT, FUTEX_IN_KERN, 0); 84 } while (res == -EINTR || d->futex == FUTEX_IN_KERN); 85 86 if (res < 0 && res != -EAGAIN) 87 stub_syscall1(__NR_exit_group, 1); 88 89 /* Try running queued syscalls. */ 90 if (syscall_handler(d) < 0 || d->restart_wait) { 91 /* Report SIGSYS if we restart. */ 92 d->signal = SIGSYS; 93 d->restart_wait = 0; 94 goto restart_wait; 95 } 96 97 /* Restore arch dependent state that is not part of the mcontext */ 98 stub_seccomp_restore_state(&d->arch_data); 99 100 /* Return so that the host modified mcontext is restored. */ 101 } 102 103 void __section(".__syscall_stub") 104 stub_signal_restorer(void) 105 { 106 /* We must not have anything on the stack when doing rt_sigreturn */ 107 stub_syscall0(__NR_rt_sigreturn); 108 } 109