[PATCH 0/1] MR10984: ntdll: Find sigreturn by recording %rip from a SIGSYS.
From: Elizabeth Figura <zfigura@codeweavers.com> The sigreturn we are attempting to exclude by finding libc.so is not in libc.so in glibc versions earlier than 2.34, but rather in libpthread.so. Therefore instead of looking for libc.so, we look for the sigreturn syscall instruction specifically, by causing it to trigger SIGSYS and recording %rip from within the handler. This approach was suggested by Paul Gofman. This fixes a regression introduced by cbb9906d75346c9ca9be8ac0b3ddb06d9d48824d. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=59726 --- dlls/ntdll/unix/signal_x86_64.c | 97 +++++++++++++++++++++++++-------- 1 file changed, 74 insertions(+), 23 deletions(-) diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index a42be942ca2..a2439f85c5f 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -35,9 +35,6 @@ #include <sys/types.h> #include <sys/mman.h> #include <unistd.h> -#ifdef HAVE_LINK_H -# include <link.h> -#endif #ifdef HAVE_MACHINE_SYSARCH_H # include <machine/sysarch.h> #endif @@ -2747,27 +2744,85 @@ static void *mac_thread_gsbase(void) #endif #ifdef __linux__ -static uintptr_t libc_addr, libc_size; -static int libc_addr_cb( struct dl_phdr_info *info, size_t size, void *arg ) +static unsigned long sigreturn_rip; +static char init_syscall_dispatch_selector = 1; /* SYSCALL_DISPATCH_FILTER_BLOCK */ + +static void sigsys_init_nested_handler( int signal, siginfo_t *siginfo, void *_sigcontext ) { - const char *p; + ucontext_t *sigcontext = _sigcontext; - if ((p = strrchr( info->dlpi_name, '/' ))) - ++p; - else - p = info->dlpi_name; + if (RAX_sig(sigcontext) == __NR_rt_sigreturn) /* sanity check */ + sigreturn_rip = RIP_sig(sigcontext); - if (strcmp( p, "libc.so.6" )) - return 0; + RIP_sig(sigcontext) -= 2; /* retry the syscall */ + + /* turn the selector off to let us get out of these handlers */ + init_syscall_dispatch_selector = 0; /* SYSCALL_DISPATCH_FILTER_ALLOW */ +} + +static void sigsys_init_handler( int signal, siginfo_t *siginfo, void *sigcontext ) +{ + struct sigaction sig_act; + + /* we need to block syscall dispatch to allow the sigaction() call */ + init_syscall_dispatch_selector = 0; /* SYSCALL_DISPATCH_FILTER_ALLOW */ - libc_addr = info->dlpi_addr; - libc_size = 0; - for (unsigned int i = 0; i < info->dlpi_phnum; ++i) - libc_size = max( libc_size, info->dlpi_phdr[i].p_vaddr + info->dlpi_phdr[i].p_memsz ); + sig_act.sa_mask = server_block_set; + sig_act.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_NODEFER; + sig_act.sa_sigaction = sigsys_init_nested_handler; + if (sigaction( SIGSYS, &sig_act, NULL ) == -1) + { + ERR_(seh)( "failed to change signal handler\n" ); + prctl( PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_OFF, 0, 0, 0 ); + syscall_dispatch_enabled = FALSE; + } - return 1; + init_syscall_dispatch_selector = 1; /* SYSCALL_DISPATCH_FILTER_BLOCK */ } + +static void find_sigreturn(void) +{ + struct sigaction sig_act; + + /* We need to exclude the sigreturn in the syscall trampoline from + * syscall user dispatch, since in the case of a signal in PE code we + * have no way to prevent it from generating SIGSYS otherwise. + * + * However, we don't know where the syscall trampoline is. + * Depending on glibc version it may be in libc or libpthread. + * Therefore we find it by triggering SIGSYS from the sigreturn at + * the end of a signal handler and using the instruction pointer. */ + + sig_act.sa_mask = server_block_set; + sig_act.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_NODEFER; + sig_act.sa_sigaction = sigsys_init_handler; + if (sigaction( SIGSYS, &sig_act, NULL ) == -1) + { + ERR_(seh)( "failed to change signal handler\n" ); + return; + } + + if (prctl( PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, 0, 0, &init_syscall_dispatch_selector ) == -1) + { + WARN_(seh)( "could not enable syscall user dispatch\n" ); + return; + } + + /* perform some syscall to get into the outer signal handler */ + syscall( __NR_close, -1 ); + + prctl( PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_OFF, 0, 0, 0 ); + + if (sigreturn_rip) + TRACE_(seh)( "found sigreturn at %#lx\n", sigreturn_rip ); + else + { + WARN_(seh)( "unable to find sigreturn\n" ); + syscall_dispatch_enabled = FALSE; + } +} + #endif /********************************************************************** @@ -2802,11 +2857,7 @@ void signal_init_process( TEB *teb ) } #ifdef __linux__ - if (syscall_dispatch_enabled && !dl_iterate_phdr( libc_addr_cb, NULL )) - { - WARN_(seh)( "could not find libc\n" ); - syscall_dispatch_enabled = FALSE; - } + find_sigreturn(); #endif alloc_syscall_frame( (frame_size + 63) & ~63 ); @@ -2866,7 +2917,7 @@ void init_syscall_frame( LPTHREAD_START_ROUTINE entry, void *arg, TEB *teb ) alloc_fs_sel( fs32_sel >> 3, get_wow_teb( teb )); } if (syscall_dispatch_enabled && prctl( PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, - libc_addr, libc_size, &thread_data->syscall_dispatch ) < 0) + sigreturn_rip, 1, &thread_data->syscall_dispatch ) < 0) WARN_(seh)( "could not enable syscall user dispatch\n" ); #elif defined (__FreeBSD__) || defined (__FreeBSD_kernel__) amd64_set_gsbase( teb ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10984
participants (2)
-
Elizabeth Figura -
Elizabeth Figura (@zfigura)