On macOS, valid syscall numbers all have a high bit set, leaving the low range "invalid" to be caught by SIGSYS.
On a Mac running macOS 14 or later, the test passes. macOS 13 and earlier have a kernel bug which prevents SIGSYS from being delivered.
This patch was adapted from Apple's Game Porting Toolkit Wine patch, and from the 'ntdll-Syscall_Emulation' wine-staging patchset.
From: Brendan Shanks bshanks@codeweavers.com
--- dlls/ntdll/tests/exception.c | 40 ++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index d173a73762d..b39b344ddfe 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -5724,6 +5724,45 @@ static void test_instrumentation_callback(void) ok( !data.call_count, "got %u.\n", data.call_count ); }
+static UINT32 find_syscall_nr(const char *function) +{ + UINT32 syscall_nr; + + char *code = (char *)GetProcAddress(hntdll, function); + + /* This assumes that Nt* syscall thunks are all formatted as: + * + * 4c 8b d1 movq %rcx, %r10 + * b8 ?? ?? ?? ?? movl $(syscall number), %eax + */ + memcpy(&syscall_nr, code + 4, sizeof(UINT32)); + return syscall_nr; +} + +static void test_direct_syscalls(void) +{ + static const BYTE code[] = + { + 0x49, 0x89, 0xd2, /* movq %rdx, %r10 */ + 0x4c, 0x89, 0xc2, /* movq %r8, %rdx */ + 0x89, 0xc8, /* movl %ecx, %eax */ + 0x0f, 0x05, /* syscall */ + 0xc3, /* ret */ + }; + + HANDLE event; + NTSTATUS (WINAPI *func)(UINT32 syscall_nr, HANDLE h, LONG *prev_state); + + event = CreateEventW(NULL, FALSE, FALSE, NULL); + memcpy(code_mem, code, sizeof(code)); + func = code_mem; + func(find_syscall_nr("NtSetEvent"), event, NULL); + + todo_wine + ok(WaitForSingleObject(event, 0) == WAIT_OBJECT_0, "Event not signaled.\n"); + CloseHandle(event); +} + #elif defined(__arm__)
static void test_thread_context(void) @@ -11870,6 +11909,7 @@ START_TEST(exception) test_raiseexception_regs(); test_hwbpt_in_syscall(); test_instrumentation_callback(); + test_direct_syscalls();
#elif defined(__aarch64__)
From: Brendan Shanks bshanks@codeweavers.com
Adapted from the 'ntdll-Syscall_Emulation' wine-staging patchset. --- dlls/ntdll/unix/signal_x86_64.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+)
diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 7a7e82077e2..537e4e1f60e 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -2239,6 +2239,34 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) }
+#ifdef __APPLE__ +/********************************************************************** + * sigsys_handler + * + * Handler for SIGSYS, signals that a non-existent system call was invoked. + * Only called on macOS 14 Sonoma and later. + */ +static void sigsys_handler( int signal, siginfo_t *siginfo, void *sigcontext ) +{ + extern const void *__wine_syscall_dispatcher_prolog_end_ptr; + ucontext_t *ucontext = init_handler( sigcontext ); + struct syscall_frame *frame = amd64_thread_data()->syscall_frame; + + TRACE_(seh)("SIGSYS, rax %#llx, rip %#llx.\n", RAX_sig(ucontext), RIP_sig(ucontext)); + + frame->rip = RIP_sig(ucontext) + 0xb; + frame->rcx = RIP_sig(ucontext); + frame->eflags = EFL_sig(ucontext); + frame->restore_flags = 0; + if (instrumentation_callback) frame->restore_flags |= RESTORE_FLAGS_INSTRUMENTATION; + RCX_sig(ucontext) = (ULONG_PTR)frame; + R11_sig(ucontext) = frame->eflags; + EFL_sig(ucontext) &= ~0x100; /* clear single-step flag */ + RIP_sig(ucontext) = (ULONG64)__wine_syscall_dispatcher_prolog_end_ptr; +} +#endif + + /*********************************************************************** * LDT support */ @@ -2532,6 +2560,10 @@ 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 return;
error:
How does macOS support this without the need for seccomp or syscall user dispatch (like on Linux)?
On Wed Nov 6 01:19:35 2024 +0000, Aida Jonikienė wrote:
How does macOS support this without the need for seccomp or syscall user dispatch (like on Linux)?
Linux and Wine syscall numbers both start at 0, so seccomp or syscall user dispatch is required to differentiate whether PE or ELF code is calling a syscall.
macOS syscall numbers don't start at 0, they [have a "class"](https://stackoverflow.com/questions/48845697/macos-64-bit-system-call-table) in the top 24 bits: they start at 0x2000000 and increment from there.
On macOS syscall numbers starting at 0 are invalid, so calling them triggers SIGSYS.
On Wed Nov 6 06:30:04 2024 +0000, Brendan Shanks wrote:
Linux and Wine syscall numbers both start at 0, so seccomp or syscall user dispatch is required to differentiate whether PE or ELF code is calling a syscall. macOS syscall numbers don't start at 0, they [have a "class"](https://stackoverflow.com/questions/48845697/macos-64-bit-system-call-table) in the top 24 bits: they start at 0x2000000 and increment from there. On macOS syscall numbers starting at 0 are invalid, so calling them triggers SIGSYS.
So that's another lucky thing that macOS has (first it was that arbitrary memory mapping for OpenGL WoW64 support and now it's this behavior)