272 lines
8.7 KiB
Diff
272 lines
8.7 KiB
Diff
From 2c628d0b8011bab4cb083c39474d20ac45c19848 Mon Sep 17 00:00:00 2001
|
|
From: Paul Gofman <pgofman@codeweavers.com>
|
|
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 <assert.h>
|
|
+#include <errno.h>
|
|
#include <pthread.h>
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
@@ -34,6 +35,8 @@
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#include <sys/mman.h>
|
|
+#include <sys/stat.h>
|
|
+#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#ifdef HAVE_MACHINE_SYSARCH_H
|
|
# include <machine/sysarch.h>
|
|
@@ -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 <linux/filter.h>
|
|
+# include <linux/seccomp.h>
|
|
+# include <sys/prctl.h>
|
|
+# include <linux/audit.h>
|
|
+#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
|
|
|