From 2c628d0b8011bab4cb083c39474d20ac45c19848 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 14 Jul 2020 15:00:34 +0300 Subject: [PATCH] ntdll: Support x86_64 syscall emulation. --- configure.ac | 1 + dlls/ntdll/unix/signal_x86_64.c | 203 ++++++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+) diff --git a/configure.ac b/configure.ac index d0ea876891d..f55d0745a23 100644 --- a/configure.ac +++ b/configure.ac @@ -694,6 +694,7 @@ AC_CHECK_HEADERS(\ linux/memfd.h \ linux/ntsync.h \ linux/param.h \ + linux/seccomp.h \ linux/serial.h \ linux/types.h \ linux/ucdrom.h \ diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 740df80341f..064af0015a4 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -27,6 +27,7 @@ #include "config.h" #include +#include #include #include #include @@ -34,6 +35,8 @@ #include #include #include +#include +#include #include #ifdef HAVE_MACHINE_SYSARCH_H # include @@ -73,6 +76,14 @@ extern void _thread_set_tsd_base(uint64_t); #endif +#if defined(HAVE_LINUX_FILTER_H) && defined(HAVE_LINUX_SECCOMP_H) && defined(HAVE_SYS_PRCTL_H) +#define HAVE_SECCOMP 1 +# include +# include +# include +# include +#endif + #include "ntstatus.h" #include "windef.h" #include "winternl.h" @@ -2012,6 +2023,194 @@ static inline DWORD is_privileged_instr( CONTEXT *context ) return 0; } +#ifdef HAVE_SECCOMP +static void sigsys_handler( int signal, siginfo_t *siginfo, void *sigcontext ) +{ + extern const void *__wine_syscall_dispatcher_prolog_end_ptr; + struct syscall_frame *frame = get_syscall_frame(); + ucontext_t *ctx = sigcontext; + + TRACE_(seh)("SIGSYS, rax %#llx, rip %#llx.\n", ctx->uc_mcontext.gregs[REG_RAX], + ctx->uc_mcontext.gregs[REG_RIP]); + + if (ctx->uc_mcontext.gregs[REG_RAX] == 0xffff) + { + /* Test syscall from the Unix side (install_bpf). */ + ctx->uc_mcontext.gregs[REG_RAX] = STATUS_INVALID_PARAMETER; + return; + } + + frame->rip = ctx->uc_mcontext.gregs[REG_RIP] + 0xb; + frame->rcx = ctx->uc_mcontext.gregs[REG_RIP]; + frame->eflags = ctx->uc_mcontext.gregs[REG_EFL]; + frame->restore_flags = 0; + ctx->uc_mcontext.gregs[REG_RCX] = (ULONG_PTR)frame; + ctx->uc_mcontext.gregs[REG_R11] = frame->eflags; + ctx->uc_mcontext.gregs[REG_EFL] &= ~0x100; /* clear single-step flag */ + ctx->uc_mcontext.gregs[REG_RIP] = (ULONG64)__wine_syscall_dispatcher_prolog_end_ptr; +} +#endif + +#ifdef HAVE_SECCOMP +static int sc_seccomp(unsigned int operation, unsigned int flags, void *args) +{ +#ifndef __NR_seccomp +# define __NR_seccomp 317 +#endif + return syscall(__NR_seccomp, operation, flags, args); +} +#endif + +static void check_bpf_jit_enable(void) +{ + char enabled; + int fd; + + fd = open("/proc/sys/net/core/bpf_jit_enable", O_RDONLY); + if (fd == -1) + { + WARN_(seh)("Could not open /proc/sys/net/core/bpf_jit_enable.\n"); + return; + } + + if (read(fd, &enabled, sizeof(enabled)) == sizeof(enabled)) + { + TRACE_(seh)("enabled %#x.\n", enabled); + + if (enabled != '1') + ERR_(seh)("BPF JIT is not enabled in the kernel, enable it to reduce syscall emulation overhead.\n"); + } + else + { + WARN_(seh)("Could not read /proc/sys/net/core/bpf_jit_enable.\n"); + } + close(fd); +} + +static void install_bpf(struct sigaction *sig_act) +{ +#ifdef HAVE_SECCOMP +# ifndef SECCOMP_FILTER_FLAG_SPEC_ALLOW +# define SECCOMP_FILTER_FLAG_SPEC_ALLOW (1UL << 2) +# endif + +# ifndef SECCOMP_SET_MODE_FILTER +# define SECCOMP_SET_MODE_FILTER 1 +# endif + static const BYTE syscall_trap_test[] = + { + 0x48, 0x89, 0xf8, /* mov %rdi, %rax */ + 0x0f, 0x05, /* syscall */ + 0xc3, /* retq */ + }; + static const unsigned int flags = SECCOMP_FILTER_FLAG_SPEC_ALLOW; + +#define NATIVE_SYSCALL_ADDRESS_START 0x700000000000 + + static struct sock_filter filter[] = + { + /* Allow i386. */ + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, arch)), + BPF_JUMP (BPF_JMP | BPF_JEQ | BPF_K, AUDIT_ARCH_X86_64, 1, 0), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + /* Native libs are loaded at high addresses. */ + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, instruction_pointer) + 4), + BPF_JUMP(BPF_JMP | BPF_JGT | BPF_K, NATIVE_SYSCALL_ADDRESS_START >> 32, 0, 8), + /* High addresses may be top-down allocations, trap those */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x7fff, 1, 0), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, instruction_pointer)), + BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, 0xfe000000, 1, 0), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, 0xffff0000, 0, 1), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRAP), + /* Allow wine64-preloader */ + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, instruction_pointer)), + BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, 0x7d400000, 1, 0), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRAP), + BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, 0x7d402000, 0, 1), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRAP), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + }; + long (*test_syscall)(long sc_number); + struct syscall_frame *frame = get_syscall_frame(); + struct sock_fprog prog; + NTSTATUS status; + + if ((ULONG_PTR)sc_seccomp < NATIVE_SYSCALL_ADDRESS_START + || (ULONG_PTR)syscall < NATIVE_SYSCALL_ADDRESS_START) + { + ERR_(seh)("Native libs are being loaded in low addresses, sc_seccomp %p, syscall %p, not installing seccomp.\n", + sc_seccomp, syscall); + ERR_(seh)("The known reasons are /proc/sys/vm/legacy_va_layout set to 1 or 'ulimit -s' being 'unlimited'.\n"); + return; + } + + sig_act->sa_sigaction = sigsys_handler; + memset(&prog, 0, sizeof(prog)); + + sigaction(SIGSYS, sig_act, NULL); + + + + + test_syscall = mmap((void *)0x600000000000, 0x1000, PROT_EXEC | PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0); + if (test_syscall != (void *)0x600000000000) + { + int ret; + + ERR("Could not allocate test syscall, falling back to seccomp presence check, test_syscall %p, errno %d.\n", + test_syscall, errno); + if (test_syscall != MAP_FAILED) munmap(test_syscall, 0x1000); + + if ((ret = prctl(PR_GET_SECCOMP, 0, NULL, 0, 0))) + { + if (ret == 2) + TRACE_(seh)("Seccomp filters already installed.\n"); + else + ERR_(seh)("Seccomp filters cannot be installed, ret %d, error %s.\n", ret, strerror(errno)); + return; + } + } + else + { + memcpy(test_syscall, syscall_trap_test, sizeof(syscall_trap_test)); + status = test_syscall(0xffff); + munmap(test_syscall, 0x1000); + if (status == STATUS_INVALID_PARAMETER) + { + TRACE_(seh)("Seccomp filters already installed.\n"); + return; + } + if (status != -ENOSYS && (status != -1 || errno != ENOSYS)) + { + ERR_(seh)("Unexpected status %#x, errno %d.\n", status, errno); + return; + } + } + + TRACE_(seh)("Installing seccomp filters.\n"); + + prog.len = ARRAY_SIZE(filter); + prog.filter = filter; + + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) + { + ERR_(seh)("prctl(PR_SET_NO_NEW_PRIVS, ...): %s.\n", strerror(errno)); + return; + } + if (sc_seccomp(SECCOMP_SET_MODE_FILTER, flags, &prog)) + { + ERR_(seh)("prctl(PR_SET_SECCOMP, ...): %s.\n", strerror(errno)); + return; + } + check_bpf_jit_enable(); +#else + WARN_(seh)("Built without seccomp.\n"); +#endif +} /*********************************************************************** * handle_interrupt @@ -2879,10 +3078,14 @@ void signal_init_process(void) if (sigaction( SIGSEGV, &sig_act, NULL ) == -1) goto error; if (sigaction( SIGILL, &sig_act, NULL ) == -1) goto error; if (sigaction( SIGBUS, &sig_act, NULL ) == -1) goto error; + #ifdef __APPLE__ sig_act.sa_sigaction = sigsys_handler; if (sigaction( SIGSYS, &sig_act, NULL ) == -1) goto error; #endif + + install_bpf(&sig_act); + return; error: -- 2.51.0