Files
main/hardware/emulator/wine/files/0001-ntdll-Support-x86_64-syscall-emulation.patch
2026-03-31 16:59:49 +03:00

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