blob: 19baac51684e3f532045936b87f2ca109e35b264 [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2011-2020 Google, Inc. All rights reserved.
* Copyright (c) 2000-2010 VMware, Inc. All rights reserved.
* **********************************************************/
/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of VMware, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
/* Copyright (c) 2003-2007 Determina Corp. */
/* Copyright (c) 2001-2003 Massachusetts Institute of Technology */
/* Copyright (c) 2000-2001 Hewlett-Packard Company */
/*
* signal.c - dynamorio signal handler
*/
#include <errno.h>
#undef errno
#include "signal_private.h" /* pulls in globals.h for us, in right order */
/* We want to build on older toolchains so we have our own copy of signal
* data structures
*/
#include "include/siginfo.h"
#ifdef LINUX
# include "include/sigcontext.h"
# include "include/signalfd.h"
# include "../globals.h" /* after our sigcontext.h, to preclude bits/sigcontext.h */
#elif defined(MACOS)
# include "../globals.h" /* this defines _XOPEN_SOURCE for Mac */
# include <signal.h> /* after globals.h, for _XOPEN_SOURCE from os_exports.h */
#endif
#ifdef LINUX
# include <linux/sched.h>
#endif
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ucontext.h>
#include "os_private.h"
#include "../fragment.h"
#include "../fcache.h"
#include "../perfctr.h"
#include "arch.h"
#include "../monitor.h" /* for trace_abort */
#include "../link.h" /* for linking interrupted fragment_t */
#include "instr.h" /* to find target of SIGSEGV */
#include "decode.h" /* to find target of SIGSEGV */
#include "decode_fast.h" /* to handle self-mod code */
#include "../synch.h"
#include "../nudge.h"
#include "disassemble.h"
#include "ksynch.h"
#include "tls.h" /* tls_reinstate_selector */
#include "../translate.h"
#ifdef LINUX
# include "include/syscall.h"
#else
# include <sys/syscall.h>
#endif
#ifdef CLIENT_INTERFACE
# include "instrument.h"
#endif
#ifdef VMX86_SERVER
# include <errno.h>
#endif
/* Define the Linux names, which the code is already using */
#ifndef SA_NOMASK
# define SA_NOMASK SA_NODEFER
#endif
#ifndef SA_ONESHOT
# define SA_ONESHOT SA_RESETHAND
#endif
#ifndef SS_AUTODISARM
# define SS_AUTODISARM (1U << 31)
#endif
#ifndef SS_FLAG_BITS
# define SS_FLAG_BITS SS_AUTODISARM
#endif
#ifdef X86
/* Kernel-only flags. */
# define SA_IA32_ABI 0x02000000U
# define SA_X32_ABI 0x01000000U
#endif
/**** data structures ***************************************************/
/* The signal numbers are slightly different between operating systems.
* To support differing default actions, we have separate arrays, rather
* than indirecting to a single all-signals array.
*/
extern int default_action[];
/* We know that many signals are always asynchronous.
* Others, however, may be synchronous or may not -- e.g., another process
* could send us a SIGSEGV, and there is no way we can tell whether it
* was generated by a real memory fault or not. Thus we have to assume
* that we must not delay any SIGSEGV deliveries.
*/
extern bool can_always_delay[];
static inline bool
sig_is_alarm_signal(int sig)
{
return (sig == SIGALRM || sig == SIGVTALRM || sig == SIGPROF);
}
/* we do not use SIGSTKSZ b/c for things like code modification
* we end up calling many core routines and so want more space
* (though currently non-debug stack size == SIGSTKSZ (8KB))
*/
#define SIGSTACK_SIZE (DYNAMO_OPTION(signal_stack_size))
/* this flag not defined in our headers */
#define SA_RESTORER 0x04000000
/* if no app sigaction, it's RT, since that's our handler */
#ifdef LINUX
# define IS_RT_FOR_APP(info, sig) \
IF_X64_ELSE(true, \
((info)->app_sigaction[(sig)] == NULL \
? true \
: (TEST(SA_SIGINFO, (info)->app_sigaction[(sig)]->flags))))
#elif defined(MACOS)
# define IS_RT_FOR_APP(info, sig) (true)
#endif
/* kernel sets size and sp to 0 for SS_DISABLE
* when asked, will hand back SS_ONSTACK only if current xsp is inside the
* alt stack; otherwise, if an alt stack is registered, it will give flags of 0
* We do not support the "legacy stack switching" that uses the restorer field
* as seen in kernel sources.
*/
#define APP_HAS_SIGSTACK(info) \
((info)->app_sigstack.ss_sp != NULL && (info)->app_sigstack.ss_flags != SS_DISABLE)
/* Under normal circumstances the app_sigaction is lazily initialized when the
* app registers a signal handler, but during detach there are points where we
* are still intercepting signals after app_sigaction has been set to
* zeros. To be extra defensive, we do a NULL check.
*/
#define USE_APP_SIGSTACK(info, sig) \
(APP_HAS_SIGSTACK(info) && (info)->app_sigaction[sig] != NULL && \
TEST(SA_ONSTACK, (info)->app_sigaction[sig]->flags))
/* If we only intercept a few signals, we leave whether un-intercepted signals
* are blocked unchanged and stored in the kernel. If we intercept all (not
* quite yet: PR 297033, hence the need for this macro) we emulate the mask for
* all.
*/
#define EMULATE_SIGMASK(info, sig) \
(DYNAMO_OPTION(intercept_all_signals) || (info)->we_intercept[(sig)])
/* i#27: custom data to pass to the child of a clone */
/* PR i#149/403015: clone record now passed via a new dstack */
typedef struct _clone_record_t {
byte *dstack; /* dstack for new thread - allocated by parent thread */
#ifdef MACOS
/* XXX i#1403: once we have lower-level, earlier thread interception we can
* likely switch to something closer to what we do on Linux.
* This is used for bsdthread_create, where app_thread_xsp is NULL;
* for vfork, app_thread_xsp is non-NULL and this is unused.
*/
void *thread_arg;
#endif
reg_t app_thread_xsp; /* app xsp preserved for new thread to use */
app_pc continuation_pc;
thread_id_t caller_id;
int clone_sysnum;
uint clone_flags;
thread_sig_info_t info;
thread_sig_info_t *parent_info;
void *pcprofile_info;
#ifdef AARCHXX
/* To ensure we have the right value as of the point of the clone, we
* store it here (we'll have races if we try to get it during new thread
* init).
*/
reg_t app_stolen_value;
# ifndef AARCH64
dr_isa_mode_t isa_mode;
# endif
/* To ensure we have the right app lib tls base in child thread,
* we store it here if necessary (clone w/o CLONE_SETTLS or vfork).
*/
void *app_lib_tls_base;
#endif
/* we leave some padding at base of stack for dynamorio_clone
* to store values
*/
reg_t for_dynamorio_clone[4];
} __attribute__((__aligned__(ABI_STACK_ALIGNMENT))) clone_record_t;
/* i#350: set up signal handler for safe_read/faults during init */
static thread_sig_info_t init_info;
static kernel_sigset_t init_sigmask;
#ifdef DEBUG
static bool removed_sig_handler;
#endif
os_cxt_ptr_t osc_empty;
/* For delivering signals that arrive in now-native threads while we're still
* detaching other threads. The thread that receives the signal has no dcontext
* anymore, so we need a global record of its handler (DR's handler is still registered
* with the kernel).
* XXX i#1921: To properly handle multiple separate signal handler thread groups
* within this single DR process domain, we would need a list of these, along with
* lists of threads within each. Since that is very rare, and requires either
* hardcoded maximums or complexities with exit-time heap, we currently do not
* support that and die if asked to deliver a native signal in such circumstances.
*/
DECLARE_CXTSWPROT_VAR(static read_write_lock_t detached_sigact_lock,
INIT_READWRITE_LOCK(detached_sigact_lock));
static kernel_sigaction_t detached_sigact[SIGARRAY_SIZE];
static bool multiple_handlers_present; /* Accessed using atomics, not the lock. */
/**** function prototypes ***********************************************/
/* in x86.asm */
void
master_signal_handler(int sig, kernel_siginfo_t *siginfo, kernel_ucontext_t *ucxt);
static void
set_handler_and_record_app(dcontext_t *dcontext, thread_sig_info_t *info, int sig,
kernel_sigaction_t *act);
static void
intercept_signal(dcontext_t *dcontext, thread_sig_info_t *info, int sig);
static void
signal_info_init_sigaction(dcontext_t *dcontext, thread_sig_info_t *info);
static void
signal_info_exit_sigaction(dcontext_t *dcontext, thread_sig_info_t *info,
bool other_thread);
static bool
execute_handler_from_cache(dcontext_t *dcontext, int sig, sigframe_rt_t *our_frame,
sigcontext_t *sc_orig,
fragment_t *f _IF_CLIENT(byte *access_address));
static bool
execute_handler_from_dispatch(dcontext_t *dcontext, int sig);
static void
execute_native_handler(dcontext_t *dcontext, int sig, sigframe_rt_t *our_frame);
/* Execute default action from code cache and may terminate the process.
* If returns, the return value decides if caller should restore
* the untranslated context.
*/
static bool
execute_default_from_cache(dcontext_t *dcontext, int sig, sigframe_rt_t *frame,
sigcontext_t *sc_orig, bool forged);
static void
execute_default_from_dispatch(dcontext_t *dcontext, int sig, sigframe_rt_t *frame);
static bool
handle_alarm(dcontext_t *dcontext, int sig, kernel_ucontext_t *ucxt);
static bool
handle_suspend_signal(dcontext_t *dcontext, kernel_ucontext_t *ucxt,
sigframe_rt_t *frame);
static bool
handle_nudge_signal(dcontext_t *dcontext, kernel_siginfo_t *siginfo,
kernel_ucontext_t *ucxt);
static void
init_itimer(dcontext_t *dcontext, bool first);
static bool
set_actual_itimer(dcontext_t *dcontext, int which, thread_sig_info_t *info, bool enable);
static bool
alarm_signal_has_DR_only_itimer(dcontext_t *dcontext, int signal);
#ifdef DEBUG
static void
dump_sigset(dcontext_t *dcontext, kernel_sigset_t *set);
#endif
static bool
is_sys_kill(dcontext_t *dcontext, byte *pc, byte *xsp, kernel_siginfo_t *info);
int
sigaction_syscall(int sig, kernel_sigaction_t *act, kernel_sigaction_t *oact)
{
#if !defined(VMX86_SERVER) && defined(LINUX)
/* PR 305020: must have SA_RESTORER for x64 */
/* i#2812: must have SA_RESTORER to handle vsyscall32 being disabled */
if (act != NULL && !TEST(SA_RESTORER, act->flags)) {
act->flags |= SA_RESTORER;
act->restorer = (void (*)(void))dynamorio_sigreturn;
}
#endif
return dynamorio_syscall(IF_MACOS_ELSE(SYS_sigaction, SYS_rt_sigaction), 4, sig, act,
oact, sizeof(kernel_sigset_t));
}
static inline bool
signal_is_interceptable(int sig)
{
return (sig != SIGKILL && sig != SIGSTOP);
}
static inline int
sigaltstack_syscall(const stack_t *newstack, stack_t *oldstack)
{
return dynamorio_syscall(SYS_sigaltstack, 2, newstack, oldstack);
}
static inline int
getitimer_syscall(int which, struct itimerval *val)
{
return dynamorio_syscall(SYS_getitimer, 2, which, val);
}
static inline int
setitimer_syscall(int which, struct itimerval *val, struct itimerval *old)
{
return dynamorio_syscall(SYS_setitimer, 3, which, val, old);
}
static inline int
sigprocmask_syscall(int how, kernel_sigset_t *set, kernel_sigset_t *oset,
size_t sigsetsize)
{
return dynamorio_syscall(IF_MACOS_ELSE(SYS_sigprocmask, SYS_rt_sigprocmask), 4, how,
set, oset, sigsetsize);
}
void
block_all_noncrash_signals_except(kernel_sigset_t *oset, int num_signals,
... /* list of signals */)
{
kernel_sigset_t set;
kernel_sigfillset(&set);
va_list ap;
va_start(ap, num_signals);
for (int i = 0; i < num_signals; ++i) {
kernel_sigdelset(&set, va_arg(ap, int));
}
va_end(ap);
/* We never block SIGSEGV or SIGBUS: we need them for various safe reads and to
* properly report crashes.
*/
kernel_sigdelset(&set, SIGSEGV);
kernel_sigdelset(&set, SIGBUS);
sigprocmask_syscall(SIG_SETMASK, &set, oset, sizeof(set));
}
static void
unblock_all_signals(kernel_sigset_t *oset)
{
kernel_sigset_t set;
kernel_sigemptyset(&set);
sigprocmask_syscall(SIG_SETMASK, &set, oset, sizeof(set));
}
/* exported for stackdump.c */
bool
set_default_signal_action(int sig)
{
kernel_sigset_t set;
kernel_sigaction_t act;
int rc;
memset(&act, 0, sizeof(act));
act.handler = (handler_t)SIG_DFL;
/* arm the signal */
rc = sigaction_syscall(sig, &act, NULL);
DODEBUG({ removed_sig_handler = true; });
/* If we're in our handler now, we have to unblock */
kernel_sigemptyset(&set);
kernel_sigaddset(&set, sig);
sigprocmask_syscall(SIG_UNBLOCK, &set, NULL, sizeof(set));
return (rc == 0);
}
static bool
set_ignore_signal_action(int sig)
{
kernel_sigaction_t act;
int rc;
memset(&act, 0, sizeof(act));
act.handler = (handler_t)SIG_IGN;
/* arm the signal */
rc = sigaction_syscall(sig, &act, NULL);
return (rc == 0);
}
/* We assume that signal handlers will be shared most of the time
* (pthreads shares them)
* Rather than start out with the handler table in local memory and then
* having to transfer to global, we just always use global
*/
static void
handler_free(dcontext_t *dcontext, void *p, size_t size)
{
global_heap_free(p, size HEAPACCT(ACCT_OTHER));
}
static void *
handler_alloc(dcontext_t *dcontext, size_t size)
{
return global_heap_alloc(size HEAPACCT(ACCT_OTHER));
}
/* Does not return. */
static void
report_unhandleable_signal_and_exit(int sig, const char *sub_message)
{
/* TODO i#1921: Add more info such as the PC and disasm of surrounding instrs. */
char signum_str[8];
snprintf(signum_str, BUFFER_SIZE_ELEMENTS(signum_str), "%d", sig);
NULL_TERMINATE_BUFFER(signum_str);
char tid_str[16];
snprintf(tid_str, BUFFER_SIZE_ELEMENTS(tid_str), TIDFMT, get_sys_thread_id());
NULL_TERMINATE_BUFFER(tid_str);
REPORT_FATAL_ERROR_AND_EXIT(FAILED_TO_HANDLE_SIGNAL, 5, get_application_name(),
get_application_pid(), signum_str, tid_str, sub_message);
}
/**** top-level routines ***********************************************/
static bool
os_itimers_thread_shared(void)
{
static bool itimers_shared;
static bool cached = false;
if (!cached) {
file_t f = os_open("/proc/version", OS_OPEN_READ);
if (f != INVALID_FILE) {
char buf[128];
int major, minor, rel;
os_read(f, buf, BUFFER_SIZE_ELEMENTS(buf));
NULL_TERMINATE_BUFFER(buf);
if (sscanf(buf, "%*s %*s %d.%d.%d", &major, &minor, &rel) == 3) {
/* Linux NPTL in kernel 2.6.12+ has POSIX-style itimers shared
* among threads.
*/
LOG(GLOBAL, LOG_ASYNCH, 1, "kernel version = %d.%d.%d\n", major, minor,
rel);
itimers_shared = ((major == 2 && minor >= 6 && rel >= 12) ||
(major >= 3 /* linux-3.0 or above */));
cached = true;
}
os_close(f);
}
if (!cached) {
/* assume not shared */
itimers_shared = false;
cached = true;
}
LOG(GLOBAL, LOG_ASYNCH, 1, "itimers are %s\n",
itimers_shared ? "thread-shared" : "thread-private");
}
return itimers_shared;
}
static void
unset_initial_crash_handlers(dcontext_t *dcontext)
{
ASSERT(init_info.app_sigaction != NULL);
signal_info_exit_sigaction(GLOBAL_DCONTEXT, &init_info, false /*!other_thread*/);
/* Undo the unblock-all */
sigprocmask_syscall(SIG_SETMASK, &init_sigmask, NULL, sizeof(init_sigmask));
DOLOG(2, LOG_ASYNCH, {
LOG(THREAD, LOG_ASYNCH, 2, "initial app signal mask:\n");
dump_sigset(dcontext, &init_sigmask);
});
}
void
d_r_signal_init(void)
{
kernel_sigset_t set;
IF_LINUX(IF_X86_64(ASSERT(ALIGNED(offsetof(sigpending_t, xstate), AVX_ALIGNMENT))));
IF_MACOS(ASSERT(sizeof(kernel_sigset_t) == sizeof(__darwin_sigset_t)));
os_itimers_thread_shared();
/* Set up a handler for safe_read (or other fault detection) during
* DR init before thread is initialized.
*
* XXX: could set up a clone_record_t and pass to the initial
* signal_thread_inherit() but that would require further code changes.
* Could also call signal_thread_inherit to init this, but we don't want
* to intercept timer signals, etc. before we're ready to handle them,
* so we do a partial init.
*/
signal_info_init_sigaction(GLOBAL_DCONTEXT, &init_info);
intercept_signal(GLOBAL_DCONTEXT, &init_info, SIGSEGV);
intercept_signal(GLOBAL_DCONTEXT, &init_info, SIGBUS);
kernel_sigemptyset(&set);
kernel_sigaddset(&set, SIGSEGV);
kernel_sigaddset(&set, SIGBUS);
sigprocmask_syscall(SIG_UNBLOCK, &set, &init_sigmask, sizeof(set));
IF_LINUX(signalfd_init());
signal_arch_init();
}
void
d_r_signal_exit()
{
DELETE_READWRITE_LOCK(detached_sigact_lock);
IF_LINUX(signalfd_exit());
if (init_info.app_sigaction != NULL) {
/* We never took over the app (e.g., standalone mode). Restore its state. */
unset_initial_crash_handlers(GLOBAL_DCONTEXT);
}
#ifdef DEBUG
if (d_r_stats->loglevel > 0 && (d_r_stats->logmask & (LOG_ASYNCH | LOG_STATS)) != 0) {
LOG(GLOBAL, LOG_ASYNCH | LOG_STATS, 1, "Total signals delivered: %d\n",
GLOBAL_STAT(num_signals));
}
#endif
}
#ifdef HAVE_SIGALTSTACK
/* Separated out to run from the dstack (i#2016: see below). */
static void
set_our_alt_stack(void *arg)
{
thread_sig_info_t *info = (thread_sig_info_t *)arg;
DEBUG_DECLARE(int rc =)
sigaltstack_syscall(&info->sigstack, &info->app_sigstack);
ASSERT(rc == 0);
}
#endif
void
signal_thread_init(dcontext_t *dcontext, void *os_data)
{
thread_sig_info_t *info =
HEAP_TYPE_ALLOC(dcontext, thread_sig_info_t, ACCT_OTHER, PROTECTED);
size_t pend_unit_size = sizeof(sigpending_t) +
/* include alignment for xsave on xstate */
signal_frame_extra_size(true)
/* sigpending_t has xstate inside it already */
IF_LINUX(IF_X86(-sizeof(kernel_xstate_t)));
IF_X86(ASSERT(YMM_ENABLED() || !ZMM_ENABLED()));
/* pend_unit_size may not be aligned, even for AVX. We request alignment from the
* allocator for all pending units (xref i#3749, i#3380).
*/
/* all fields want to be initialized to 0 */
memset(info, 0, sizeof(thread_sig_info_t));
dcontext->signal_field = (void *)info;
/* our special heap to avoid reentrancy problems
* composed entirely of sigpending_t units
* Note that it's fine to have the special heap do page-at-a-time
* committing, which does not use locks (unless triggers reset!),
* but if we need a new unit that will grab a lock: we try to
* avoid that by limiting the # of pending alarm signals (PR 596768).
*/
info->sigheap = special_heap_init_aligned(
pend_unit_size, IF_X86_ELSE(AVX_ALIGNMENT, 0),
false /* cannot have any locking */, false /* -x */, true /* persistent */,
pend_unit_size * DYNAMO_OPTION(max_pending_signals));
#ifdef HAVE_SIGALTSTACK
/* set up alternate stack
* i#552 we may terminate the process without freeing the stack, so we
* stack_alloc it to exempt from the memory leak check.
*/
info->sigstack.ss_sp = (char *)stack_alloc(SIGSTACK_SIZE, NULL) - SIGSTACK_SIZE;
info->sigstack.ss_size = SIGSTACK_SIZE;
/* kernel will set xsp to sp+size to grow down from there, we don't have to */
info->sigstack.ss_flags = 0;
/* i#2016: for late takeover, this app thread may already be on its own alt
* stack. Not setting SA_ONSTACK for SUSPEND_SIGNAL is not sufficient to avoid
* this, as our SUSPEND_SIGNAL can interrupt the app inside its own signal
* handler. Thus, we simply swap to another stack temporarily to avoid the
* kernel complaining. The dstack is set up but it has the clone record and
* initial mcxt, so we use the new alt stack.
*/
call_switch_stack((void *)info, (byte *)info->sigstack.ss_sp + info->sigstack.ss_size,
set_our_alt_stack, NULL, true /*return*/);
LOG(THREAD, LOG_ASYNCH, 1, "signal stack is " PFX " - " PFX "\n",
info->sigstack.ss_sp, info->sigstack.ss_sp + info->sigstack.ss_size);
/* app_sigstack dealt with below, based on parentage */
#endif
kernel_sigemptyset(&info->app_sigblocked);
ASSIGN_INIT_LOCK_FREE(info->child_lock, child_lock);
/* signal_thread_inherit() finishes per-thread init and is invoked
* by os_thread_init_finalize(): we need it after synch_thread_init() and
* other post-os_thread_init() setup b/c we can't yet record pending signals,
* but we need it before we give up thread_initexit_lock so we can handle
* our own suspend signals (i#2779).
*/
}
bool
is_thread_signal_info_initialized(dcontext_t *dcontext)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
return info->fully_initialized;
}
/* i#27: create custom data to pass to the child of a clone
* since we can't rely on being able to find the caller, or that
* its syscall data is still valid, once in the child.
*
* i#149/ PR 403015: The clone record is passed to the new thread via the dstack
* created for it. Unlike before, where the child thread would create its own
* dstack, now the parent thread creates the dstack. Also, switches app stack
* to dstack.
*
* XXX i#1403: for Mac we want to eventually do lower-level earlier interception
* of threads, but for now we're later and higher-level, intercepting the user
* thread function on the new thread's stack. We ignore app_thread_xsp.
*/
void *
#ifdef MACOS
create_clone_record(dcontext_t *dcontext, reg_t *app_thread_xsp, app_pc thread_func,
void *thread_arg)
#else
create_clone_record(dcontext_t *dcontext, reg_t *app_thread_xsp)
#endif
{
clone_record_t *record;
byte *dstack = stack_alloc(DYNAMORIO_STACK_SIZE, NULL);
LOG(THREAD, LOG_ASYNCH, 1, "create_clone_record: dstack for new thread is " PFX "\n",
dstack);
#ifdef MACOS
if (app_thread_xsp == NULL) {
record = HEAP_TYPE_ALLOC(GLOBAL_DCONTEXT, clone_record_t, ACCT_THREAD_MGT,
true /*prot*/);
record->app_thread_xsp = 0;
record->continuation_pc = thread_func;
record->thread_arg = thread_arg;
record->clone_flags = CLONE_THREAD | CLONE_VM | CLONE_SIGHAND | SIGCHLD;
} else {
#endif
/* Note, the stack grows to low memory addr, so dstack points to the high
* end of the allocated stack region. So, we must subtract to get space for
* the clone record.
*/
record = (clone_record_t *)(dstack - sizeof(clone_record_t));
ASSERT(ALIGNED(record, get_ABI_stack_alignment()));
record->app_thread_xsp = *app_thread_xsp;
/* asynch_target is set in d_r_dispatch() prior to calling pre_system_call(). */
record->continuation_pc = dcontext->asynch_target;
record->clone_flags = dcontext->sys_param0;
#ifdef MACOS
}
#endif
LOG(THREAD, LOG_ASYNCH, 1, "allocated clone record: " PFX "\n", record);
record->dstack = dstack;
record->caller_id = dcontext->owning_thread;
record->clone_sysnum = dcontext->sys_num;
record->info = *((thread_sig_info_t *)dcontext->signal_field);
/* Sigstack is not inherited so clear it now to avoid having to figure out
* where it got its value in signal_thread_inherit (i#3116).
*/
memset(&record->info.app_sigstack, 0, sizeof(record->info.app_sigstack));
record->info.app_sigstack.ss_flags = SS_DISABLE;
record->parent_info = (thread_sig_info_t *)dcontext->signal_field;
record->pcprofile_info = dcontext->pcprofile_field;
#ifdef AARCHXX
record->app_stolen_value = get_stolen_reg_val(get_mcontext(dcontext));
# ifndef AARCH64
record->isa_mode = dr_get_isa_mode(dcontext);
# endif
/* If the child thread shares the same TLS with parent by not setting
* CLONE_SETTLS or vfork, we put the TLS base here and clear the
* thread register in new_thread_setup, so that DR can distinguish
* this case from normal pthread thread creation.
*/
record->app_lib_tls_base = (!TEST(CLONE_SETTLS, record->clone_flags))
? os_get_app_tls_base(dcontext, TLS_REG_LIB)
: NULL;
#endif
LOG(THREAD, LOG_ASYNCH, 1, "create_clone_record: thread " TIDFMT ", pc " PFX "\n",
record->caller_id, record->continuation_pc);
#ifdef MACOS
if (app_thread_xsp != NULL) {
#endif
/* Set the thread stack to point to the dstack, below the clone record.
* Note: it's glibc who sets up the arg to the thread start function;
* the kernel just does a fork + stack swap, so we can get away w/ our
* own stack swap if we restore before the glibc asm code takes over.
* We restore this parameter to the app value in
* restore_clone_param_from_clone_record().
*/
/* i#754: set stack to be XSTATE aligned for saving YMM registers */
ASSERT(ALIGNED(XSTATE_ALIGNMENT, REGPARM_END_ALIGN));
*app_thread_xsp = ALIGN_BACKWARD(record, XSTATE_ALIGNMENT);
#ifdef MACOS
}
#endif
return (void *)record;
}
/* This is to support dr_create_client_thread() */
void
set_clone_record_fields(void *record, reg_t app_thread_xsp, app_pc continuation_pc,
uint clone_sysnum, uint clone_flags)
{
clone_record_t *rec = (clone_record_t *)record;
ASSERT(rec != NULL);
rec->app_thread_xsp = app_thread_xsp;
rec->continuation_pc = continuation_pc;
rec->clone_sysnum = clone_sysnum;
rec->clone_flags = clone_flags;
}
/* i#149/PR 403015: The clone record is passed to the new thread by placing it
* at the bottom of the dstack, i.e., the high memory. So the new thread gets
* it from the base of the dstack. The dstack is then set as the app stack.
*
* CAUTION: don't use a lot of stack in this routine as it gets invoked on the
* dstack from new_thread_setup - this is because this routine assumes
* no more than a page of dstack has been used so far since the clone
* system call was done.
*/
void *
get_clone_record(reg_t xsp)
{
clone_record_t *record;
byte *dstack_base;
/* xsp should be in a dstack, i.e., dynamorio heap. */
ASSERT(is_dynamo_address((app_pc)xsp));
/* The (size of the clone record +
* stack used by new_thread_start (only for setting up priv_mcontext_t) +
* stack used by new_thread_setup before calling get_clone_record())
* is less than a page. This is verified by the assert below. If it does
* exceed a page, it won't happen at random during runtime, but in a
* predictable way during development, which will be caught by the assert.
* The current usage is about 800 bytes for clone_record +
* sizeof(priv_mcontext_t) + few words in new_thread_setup before
* get_clone_record() is called.
*/
dstack_base = (byte *)ALIGN_FORWARD(xsp, PAGE_SIZE);
record = (clone_record_t *)(dstack_base - sizeof(clone_record_t));
/* dstack_base and the dstack in the clone record should be the same. */
ASSERT(dstack_base == record->dstack);
#ifdef MACOS
ASSERT(record->app_thread_xsp != 0); /* else it's not in dstack */
#endif
return (void *)record;
}
/* i#149/PR 403015: App xsp is passed to the new thread via the clone record. */
reg_t
get_clone_record_app_xsp(void *record)
{
ASSERT(record != NULL);
return ((clone_record_t *)record)->app_thread_xsp;
}
#ifdef MACOS
void *
get_clone_record_thread_arg(void *record)
{
ASSERT(record != NULL);
return ((clone_record_t *)record)->thread_arg;
}
#endif
byte *
get_clone_record_dstack(void *record)
{
ASSERT(record != NULL);
return ((clone_record_t *)record)->dstack;
}
#ifdef AARCHXX
reg_t
get_clone_record_stolen_value(void *record)
{
ASSERT(record != NULL);
return ((clone_record_t *)record)->app_stolen_value;
}
# ifndef AARCH64
uint /* dr_isa_mode_t but we have a header ordering problem */
get_clone_record_isa_mode(void *record)
{
ASSERT(record != NULL);
return ((clone_record_t *)record)->isa_mode;
}
# endif
void
set_thread_register_from_clone_record(void *record)
{
/* If record->app_lib_tls_base is not NULL, it means the parent
* thread did not setup TLS for the child, and we need clear the
* thread register.
*/
if (((clone_record_t *)record)->app_lib_tls_base != NULL)
write_thread_register(NULL);
}
void
set_app_lib_tls_base_from_clone_record(dcontext_t *dcontext, void *record)
{
if (((clone_record_t *)record)->app_lib_tls_base != NULL) {
/* child and parent share the same TLS */
os_set_app_tls_base(dcontext, TLS_REG_LIB,
((clone_record_t *)record)->app_lib_tls_base);
}
}
#endif
void
restore_clone_param_from_clone_record(dcontext_t *dcontext, void *record)
{
#ifdef LINUX
ASSERT(record != NULL);
clone_record_t *crec = (clone_record_t *)record;
if (crec->clone_sysnum == SYS_clone && TEST(CLONE_VM, crec->clone_flags)) {
/* Restore the original stack parameter to the syscall, which we clobbered
* in create_clone_record(). Some apps examine it post-syscall (i#3171).
*/
set_syscall_param(dcontext, SYSCALL_PARAM_CLONE_STACK,
get_mcontext(dcontext)->xsp);
}
#endif
}
/* Initializes info's app_sigaction, restorer_valid, and we_intercept fields */
static void
signal_info_init_sigaction(dcontext_t *dcontext, thread_sig_info_t *info)
{
info->app_sigaction = (kernel_sigaction_t **)handler_alloc(
dcontext, SIGARRAY_SIZE * sizeof(kernel_sigaction_t *));
memset(info->app_sigaction, 0, SIGARRAY_SIZE * sizeof(kernel_sigaction_t *));
memset(&info->restorer_valid, -1, SIGARRAY_SIZE * sizeof(info->restorer_valid[0]));
info->we_intercept = (bool *)handler_alloc(dcontext, SIGARRAY_SIZE * sizeof(bool));
memset(info->we_intercept, 0, SIGARRAY_SIZE * sizeof(bool));
}
/* Cleans up info's app_sigaction and we_intercept entries */
static void
signal_info_exit_sigaction(dcontext_t *dcontext, thread_sig_info_t *info,
bool other_thread)
{
int i;
kernel_sigaction_t act;
memset(&act, 0, sizeof(act));
act.handler = (handler_t)SIG_DFL;
kernel_sigemptyset(&act.mask); /* does mask matter for SIG_DFL? */
for (i = 1; i <= MAX_SIGNUM; i++) {
if (sig_is_alarm_signal(i) && doing_detach &&
IF_CLIENT_INTERFACE(!standalone_library &&)
alarm_signal_has_DR_only_itimer(dcontext, i)) {
/* We ignore alarms *during* detach in signal_remove_alarm_handlers(),
* but to avoid crashing on an alarm arriving post-detach we set to
* SIG_IGN if we have an itimer and the app does not (a slight
* transparency violation to gain robustness: i#2270).
*/
set_ignore_signal_action(i);
} else if (!other_thread) {
if (info->app_sigaction[i] != NULL) {
/* Restore to old handler, but not if exiting whole process:
* else may get itimer during cleanup, so we set to SIG_IGN. We
* do this during detach in signal_remove_alarm_handlers() (and
* post-detach above).
*/
if (dynamo_exited && !doing_detach) {
info->app_sigaction[i]->handler = (handler_t)SIG_IGN;
}
LOG(THREAD, LOG_ASYNCH, 2, "\trestoring " PFX " as handler for %d\n",
info->app_sigaction[i]->handler, i);
sigaction_syscall(i, info->app_sigaction[i], NULL);
} else if (info->we_intercept[i]) {
/* restore to default */
LOG(THREAD, LOG_ASYNCH, 2, "\trestoring SIG_DFL as handler for %d\n", i);
sigaction_syscall(i, &act, NULL);
}
}
if (info->app_sigaction[i] != NULL) {
handler_free(dcontext, info->app_sigaction[i], sizeof(kernel_sigaction_t));
}
}
handler_free(dcontext, info->app_sigaction,
SIGARRAY_SIZE * sizeof(kernel_sigaction_t *));
info->app_sigaction = NULL;
handler_free(dcontext, info->we_intercept, SIGARRAY_SIZE * sizeof(bool));
info->we_intercept = NULL;
}
/* Called to finalize per-thread initialization.
* Inherited and shared fields are set up here.
* The clone_record contains the continuation pc, which is stored in dcontext->next_tag.
*/
void
signal_thread_inherit(dcontext_t *dcontext, void *clone_record)
{
clone_record_t *record = (clone_record_t *)clone_record;
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
int i;
if (record != NULL) {
LOG(THREAD, LOG_ASYNCH, 1, "continuation pc is " PFX "\n",
record->continuation_pc);
dcontext->next_tag = record->continuation_pc;
LOG(THREAD, LOG_ASYNCH, 1,
"parent tid is " TIDFMT ", parent sysnum is %d(%s), clone flags=" PIFX "\n",
record->caller_id, record->clone_sysnum,
#ifdef SYS_vfork
(record->clone_sysnum == SYS_vfork)
? "vfork"
:
#endif
(IF_LINUX(record->clone_sysnum == SYS_clone ? "clone" :) IF_MACOS(
record->clone_sysnum == SYS_bsdthread_create ? "bsdthread_create"
:) "unexpected"),
record->clone_flags);
#ifdef SYS_vfork
if (record->clone_sysnum == SYS_vfork) {
/* The above clone_flags argument is bogus.
SYS_vfork doesn't have a free register to keep the hardcoded value
see /usr/src/linux/arch/i386/kernel/process.c */
/* CHECK: is this the only place real clone flags are needed? */
record->clone_flags = CLONE_VFORK | CLONE_VM | SIGCHLD;
}
#endif
/* handlers are either inherited or shared */
if (TEST(CLONE_SIGHAND, record->clone_flags)) {
/* need to share table of handlers! */
LOG(THREAD, LOG_ASYNCH, 2, "sharing signal handlers with parent\n");
info->shared_app_sigaction = true;
info->shared_refcount = record->info.shared_refcount;
info->shared_lock = record->info.shared_lock;
info->app_sigaction = record->info.app_sigaction;
info->we_intercept = record->info.we_intercept;
d_r_mutex_lock(info->shared_lock);
(*info->shared_refcount)++;
#ifdef DEBUG
for (i = 1; i <= MAX_SIGNUM; i++) {
if (info->app_sigaction[i] != NULL) {
LOG(THREAD, LOG_ASYNCH, 2, "\thandler for signal %d is " PFX "\n", i,
info->app_sigaction[i]->handler);
}
}
#endif
d_r_mutex_unlock(info->shared_lock);
} else {
/* copy handlers */
LOG(THREAD, LOG_ASYNCH, 2, "inheriting signal handlers from parent\n");
if (!atomic_read_bool(&multiple_handlers_present))
ATOMIC_1BYTE_WRITE(&multiple_handlers_present, true, false);
info->app_sigaction = (kernel_sigaction_t **)handler_alloc(
dcontext, SIGARRAY_SIZE * sizeof(kernel_sigaction_t *));
memset(info->app_sigaction, 0, SIGARRAY_SIZE * sizeof(kernel_sigaction_t *));
for (i = 1; i <= MAX_SIGNUM; i++) {
info->restorer_valid[i] = -1; /* clear cache */
if (record->info.app_sigaction[i] != NULL) {
info->app_sigaction[i] = (kernel_sigaction_t *)handler_alloc(
dcontext, sizeof(kernel_sigaction_t));
memcpy(info->app_sigaction[i], record->info.app_sigaction[i],
sizeof(kernel_sigaction_t));
LOG(THREAD, LOG_ASYNCH, 2, "\thandler for signal %d is " PFX "\n", i,
info->app_sigaction[i]->handler);
}
}
info->we_intercept =
(bool *)handler_alloc(dcontext, SIGARRAY_SIZE * sizeof(bool));
memcpy(info->we_intercept, record->info.we_intercept,
SIGARRAY_SIZE * sizeof(bool));
d_r_mutex_lock(&record->info.child_lock);
record->info.num_unstarted_children--;
d_r_mutex_unlock(&record->info.child_lock);
/* this should be safe since parent should wait for us */
d_r_mutex_lock(&record->parent_info->child_lock);
record->parent_info->num_unstarted_children--;
d_r_mutex_unlock(&record->parent_info->child_lock);
}
/* itimers are either private or shared */
if (TEST(CLONE_THREAD, record->clone_flags) && os_itimers_thread_shared()) {
ASSERT(record->info.shared_itimer);
LOG(THREAD, LOG_ASYNCH, 2, "sharing itimers with parent\n");
info->shared_itimer = true;
info->shared_itimer_refcount = record->info.shared_itimer_refcount;
info->shared_itimer_underDR = record->info.shared_itimer_underDR;
info->itimer = record->info.itimer;
atomic_add_exchange_int((volatile int *)info->shared_itimer_refcount, 1);
/* shared_itimer_underDR will be incremented in start_itimer() */
} else {
info->shared_itimer = false;
init_itimer(dcontext, false /*!first thread*/);
}
/* rest of state is never shared.
* app_sigstack should already be in place, when we set up our sigstack
* we asked for old sigstack.
* FIXME: are current pending or blocked inherited?
*/
#ifdef MACOS
if (record->app_thread_xsp != 0) {
HEAP_TYPE_FREE(GLOBAL_DCONTEXT, record, clone_record_t, ACCT_THREAD_MGT,
true /*prot*/);
}
#endif
} else {
/* Initialize in isolation */
if (APP_HAS_SIGSTACK(info)) {
/* parent was NOT under our control, so the real sigstack we see is
* a real sigstack that was present before we took control
*/
LOG(THREAD, LOG_ASYNCH, 1, "app already has signal stack " PFX " - " PFX "\n",
info->app_sigstack.ss_sp,
info->app_sigstack.ss_sp + info->app_sigstack.ss_size);
}
signal_info_init_sigaction(dcontext, info);
info->shared_itimer = false; /* we'll set to true if a child is created */
init_itimer(dcontext, true /*first*/);
/* We split init vs start for the signal handlers and mask. We do not
* install ours until we start running the app, to avoid races like
* i#2335. We'll set them up when os_process_under_dynamorio_*() invokes
* signal_reinstate_handlers(). All we do now is mark which signals we
* want to intercept.
*/
if (DYNAMO_OPTION(intercept_all_signals)) {
/* PR 304708: to support client signal handlers without
* the complexity of per-thread and per-signal callbacks
* we always intercept all signals. We also check here
* for handlers the app registered before our init.
*/
for (i = 1; i <= MAX_SIGNUM; i++) {
/* cannot intercept KILL or STOP */
if (signal_is_interceptable(i) &&
/* FIXME PR 297033: we don't support intercepting DEFAULT_STOP /
* DEFAULT_CONTINUE signals. Once add support, update
* dr_register_signal_event() comments.
*/
default_action[i] != DEFAULT_STOP &&
default_action[i] != DEFAULT_CONTINUE)
info->we_intercept[i] = true;
}
} else {
/* we intercept the following signals ourselves: */
info->we_intercept[SIGSEGV] = true;
/* PR 313665: look for DR crashes on unaligned memory or mmap bounds */
info->we_intercept[SIGBUS] = true;
/* PR 212090: the signal we use to suspend threads */
info->we_intercept[SUSPEND_SIGNAL] = true;
#ifdef PAPI
/* use SIGPROF for updating gui so it can be distinguished from SIGVTALRM */
info->we_intercept[SIGPROF] = true;
#endif
/* vtalarm only used with pc profiling. it interferes w/ PAPI
* so arm this signal only if necessary
*/
if (INTERNAL_OPTION(profile_pcs)) {
info->we_intercept[SIGVTALRM] = true;
}
#ifdef CLIENT_INTERFACE
info->we_intercept[SIGALRM] = true;
#endif
#ifdef SIDELINE
info->we_intercept[SIGCHLD] = true;
#endif
/* i#61/PR 211530: the signal we use for nudges */
info->we_intercept[NUDGESIG_SIGNUM] = true;
}
/* should be 1st thread */
if (d_r_get_num_threads() > 1)
ASSERT_NOT_REACHED();
}
/* only when SIGVTALRM handler is in place should we start itimer (PR 537743) */
if (INTERNAL_OPTION(profile_pcs)) {
/* even if the parent thread exits, we can use a pointer to its
* pcprofile_info b/c when shared it's process-shared and is not freed
* until the entire process exits
*/
pcprofile_thread_init(dcontext, info->shared_itimer,
(record == NULL) ? NULL : record->pcprofile_info);
}
info->pre_syscall_app_sigprocmask_valid = false;
/* Assumed to be async safe. */
info->fully_initialized = true;
}
/* When taking over existing app threads, we assume they're using pthreads and
* expect to share signal handlers, memory, thread group id, etc.
* Invokes dynamo_thread_init() with the appropriate os_data.
*/
dcontext_t *
init_thread_with_shared_siginfo(priv_mcontext_t *mc, dcontext_t *takeover_dc)
{
clone_record_t crec = {
0,
};
thread_sig_info_t *parent_siginfo = (thread_sig_info_t *)takeover_dc->signal_field;
/* Create a fake clone record with the given siginfo. All threads in the
* same thread group must share signal handlers since Linux 2.5.35, but we
* have to guess at the other flags.
* FIXME i#764: If we take over non-pthreads threads, we'll need some way to
* tell if they're sharing signal handlers or not.
*/
crec.caller_id = takeover_dc->owning_thread;
#ifdef LINUX
crec.clone_sysnum = SYS_clone;
#else
ASSERT_NOT_IMPLEMENTED(false); /* FIXME i#58: NYI on Mac */
#endif
crec.clone_flags = PTHREAD_CLONE_FLAGS;
crec.parent_info = parent_siginfo;
crec.info = *parent_siginfo;
crec.pcprofile_info = takeover_dc->pcprofile_field;
IF_DEBUG(int r =)
dynamo_thread_init(NULL, mc, &crec _IF_CLIENT_INTERFACE(false));
ASSERT(r == SUCCESS);
return get_thread_private_dcontext();
}
static void
free_pending_signal(thread_sig_info_t *info, int sig)
{
sigpending_t *temp = info->sigpending[sig];
info->sigpending[sig] = temp->next;
special_heap_free(info->sigheap, temp);
info->num_pending--;
}
/* This is split from os_fork_init() so the new logfiles are available
* (xref i#189/PR 452168). It had to be after dynamo_other_thread_exit()
* called in dynamorio_fork_init() after os_fork_init() else we clean
* up data structs used in signal_thread_exit().
*/
void
signal_fork_init(dcontext_t *dcontext)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
int i;
/* Child of fork is a single thread in a new process so should
* start over w/ no sharing (xref i#190/PR 452178)
*/
if (info->shared_app_sigaction) {
info->shared_app_sigaction = false;
if (info->shared_lock != NULL) {
DELETE_LOCK(*info->shared_lock);
global_heap_free(info->shared_lock, sizeof(mutex_t) HEAPACCT(ACCT_OTHER));
}
if (info->shared_refcount != NULL)
global_heap_free(info->shared_refcount, sizeof(int) HEAPACCT(ACCT_OTHER));
info->shared_lock = NULL;
info->shared_refcount = NULL;
}
if (info->shared_itimer) {
/* itimers are not inherited across fork */
info->shared_itimer = false;
for (i = 0; i < NUM_ITIMERS; i++)
DELETE_RECURSIVE_LOCK((*info->itimer)[i].lock);
if (os_itimers_thread_shared())
global_heap_free(info->itimer, sizeof(*info->itimer) HEAPACCT(ACCT_OTHER));
else
heap_free(dcontext, info->itimer, sizeof(*info->itimer) HEAPACCT(ACCT_OTHER));
info->itimer = NULL; /* reset by init_itimer */
ASSERT(info->shared_itimer_refcount != NULL);
global_heap_free(info->shared_itimer_refcount, sizeof(int) HEAPACCT(ACCT_OTHER));
info->shared_itimer_refcount = NULL;
ASSERT(info->shared_itimer_underDR != NULL);
global_heap_free(info->shared_itimer_underDR, sizeof(int) HEAPACCT(ACCT_OTHER));
info->shared_itimer_underDR = NULL;
init_itimer(dcontext, true /*first*/);
}
info->num_unstarted_children = 0;
for (i = 1; i <= MAX_SIGNUM; i++) {
/* "A child created via fork(2) initially has an empty pending signal set" */
dcontext->signals_pending = 0;
while (info->sigpending[i] != NULL) {
free_pending_signal(info, i);
}
info->num_pending = 0;
}
if (INTERNAL_OPTION(profile_pcs)) {
pcprofile_fork_init(dcontext);
}
info->pre_syscall_app_sigprocmask_valid = false;
/* Assumed to be async safe. */
info->fully_initialized = true;
}
#ifdef DEBUG
static bool
sigsegv_handler_is_ours(void)
{
int rc;
kernel_sigaction_t oldact;
rc = sigaction_syscall(SIGSEGV, NULL, &oldact);
return (rc == 0 && oldact.handler == (handler_t)master_signal_handler);
}
#endif /* DEBUG */
#if defined(X86) && defined(LINUX)
static byte *
get_and_initialize_xstate_buffer(dcontext_t *dcontext)
{
/* See thread_sig_info_t.xstate_buf comments for why this is in TLS. */
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
if (info->xstate_buf == NULL) {
info->xstate_alloc =
heap_alloc(dcontext, signal_frame_extra_size(true) HEAPACCT(ACCT_OTHER));
info->xstate_buf = (byte *)ALIGN_FORWARD(info->xstate_alloc, XSTATE_ALIGNMENT);
ASSERT(info->xstate_alloc + signal_frame_extra_size(true) >=
info->xstate_buf + signal_frame_extra_size(false));
}
kernel_fpstate_t *fpstate = (kernel_fpstate_t *)info->xstate_buf;
/* If we pass uninitialized values for kernel_xsave_hdr_t.reserved1 through
* sigreturn, we'll get a SIGSEGV. Best to zero it all out.
*/
memset(fpstate, 0, signal_frame_extra_size(false));
fpstate->sw_reserved.extended_size = signal_frame_extra_size(false);
if (YMM_ENABLED()) { /* ZMM_ENABLED() always implies YMM_ENABLED() too. */
fpstate->sw_reserved.magic1 = FP_XSTATE_MAGIC1;
fpstate->sw_reserved.xstate_size = signal_frame_extra_size(false) -
FP_XSTATE_MAGIC2_SIZE IF_X86_32(-FSAVE_FPSTATE_PREFIX_SIZE);
uint bv_high, bv_low;
dr_xgetbv(&bv_high, &bv_low);
fpstate->sw_reserved.xstate_bv = (((uint64)bv_high) << 32) | bv_low;
*(int *)((byte *)fpstate + fpstate->sw_reserved.extended_size -
FP_XSTATE_MAGIC2_SIZE) = FP_XSTATE_MAGIC2;
} else {
fpstate->sw_reserved.magic1 = 0;
fpstate->sw_reserved.xstate_size = sizeof(kernel_fpstate_t);
fpstate->sw_reserved.xstate_bv = 0;
}
return info->xstate_buf;
}
#endif
void
signal_thread_exit(dcontext_t *dcontext, bool other_thread)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
int i;
/* i#1012: DR's signal handler should always be installed before this point.
*/
ASSERT(sigsegv_handler_is_ours() || removed_sig_handler);
while (info->num_unstarted_children > 0) {
/* must wait for children to start and copy our state
* before we destroy it!
*/
os_thread_yield();
}
/* stop_itimer() was already called by os_thread_not_under_dynamo() called
* from dynamo_thread_exit_common(). We need to leave the app itimers in place
* in case we're detaching.
*/
#if defined(X86) && defined(LINUX)
if (info->xstate_alloc != NULL) {
heap_free(dcontext, info->xstate_alloc,
signal_frame_extra_size(true) HEAPACCT(ACCT_OTHER));
}
#endif
/* FIXME: w/ shared handlers, if parent (the owner here) dies,
* can children keep living w/ a copy of the handlers?
*/
if (info->shared_app_sigaction) {
d_r_mutex_lock(info->shared_lock);
(*info->shared_refcount)--;
d_r_mutex_unlock(info->shared_lock);
}
if (!info->shared_app_sigaction || *info->shared_refcount == 0) {
LOG(THREAD, LOG_ASYNCH, 2, "signal handler cleanup:\n");
signal_info_exit_sigaction(dcontext, info, other_thread);
if (info->shared_lock != NULL) {
DELETE_LOCK(*info->shared_lock);
global_heap_free(info->shared_lock, sizeof(mutex_t) HEAPACCT(ACCT_OTHER));
}
if (info->shared_refcount != NULL)
global_heap_free(info->shared_refcount, sizeof(int) HEAPACCT(ACCT_OTHER));
}
if (info->shared_itimer) {
atomic_add_exchange_int((volatile int *)info->shared_itimer_refcount, -1);
}
if (!info->shared_itimer || *info->shared_itimer_refcount == 0) {
if (INTERNAL_OPTION(profile_pcs)) {
/* no cleanup needed for non-final thread in group */
pcprofile_thread_exit(dcontext);
}
for (i = 0; i < NUM_ITIMERS; i++)
DELETE_RECURSIVE_LOCK((*info->itimer)[i].lock);
if (os_itimers_thread_shared())
global_heap_free(info->itimer, sizeof(*info->itimer) HEAPACCT(ACCT_OTHER));
else
heap_free(dcontext, info->itimer, sizeof(*info->itimer) HEAPACCT(ACCT_OTHER));
if (info->shared_itimer_refcount != NULL) {
global_heap_free(info->shared_itimer_refcount,
sizeof(int) HEAPACCT(ACCT_OTHER));
ASSERT(info->shared_itimer_underDR != NULL);
global_heap_free(info->shared_itimer_underDR,
sizeof(int) HEAPACCT(ACCT_OTHER));
}
}
for (i = 1; i <= MAX_SIGNUM; i++) {
/* pending queue is per-thread and not shared */
while (info->sigpending[i] != NULL) {
sigpending_t *temp = info->sigpending[i];
info->sigpending[i] = temp->next;
special_heap_free(info->sigheap, temp);
}
info->num_pending = 0;
}
/* If no detach flag is set, we assume that this thread is on its way to exit.
* In order to prevent receiving signals while a thread is on its way to exit
* without a valid dcontext, signals at this stage are blocked. The exceptions
* are the suspend signal and any signal that a terminating SYS_kill may need.
* (i#2921). In this case, we do not want to restore the signal mask. For detach,
* we do need to restore the app's mask.
*/
if (!other_thread && doing_detach)
signal_swap_mask(dcontext, true /*to_app*/);
#ifdef HAVE_SIGALTSTACK
/* Remove our sigstack and restore the app sigstack if it had one. */
if (!other_thread) {
LOG(THREAD, LOG_ASYNCH, 2, "removing our signal stack " PFX " - " PFX "\n",
info->sigstack.ss_sp, info->sigstack.ss_sp + info->sigstack.ss_size);
if (APP_HAS_SIGSTACK(info)) {
LOG(THREAD, LOG_ASYNCH, 2, "restoring app signal stack " PFX " - " PFX "\n",
info->app_sigstack.ss_sp,
info->app_sigstack.ss_sp + info->app_sigstack.ss_size);
} else {
ASSERT(TEST(SS_DISABLE, info->app_sigstack.ss_flags));
}
if (info->sigstack.ss_sp != NULL) {
/* i#552: to raise client exit event, we may call dynamo_process_exit
* on sigstack in signal handler.
* In that case we set sigstack (ss_sp) NULL to avoid stack swap.
*/
# ifdef MACOS
if (info->app_sigstack.ss_sp == NULL) {
/* Kernel fails w/ ENOMEM (even for SS_DISABLE) if ss_size is too small */
info->sigstack.ss_flags = SS_DISABLE;
i = sigaltstack_syscall(&info->sigstack, NULL);
/* i#1814: kernel gives EINVAL if last handler didn't call sigreturn! */
ASSERT(i == 0 || i == -EINVAL);
} else {
i = sigaltstack_syscall(&info->app_sigstack, NULL);
/* i#1814: kernel gives EINVAL if last handler didn't call sigreturn! */
ASSERT(i == 0 || i == -EINVAL);
}
# else
i = sigaltstack_syscall(&info->app_sigstack, NULL);
ASSERT(i == 0);
# endif
}
}
#endif
IF_LINUX(signalfd_thread_exit(dcontext, info));
special_heap_exit(info->sigheap);
DELETE_LOCK(info->child_lock);
#ifdef HAVE_SIGALTSTACK
if (info->sigstack.ss_sp != NULL) {
/* i#552: to raise client exit event, we may call dynamo_process_exit
* on sigstack in signal handler.
* In that case we set sigstack (ss_sp) NULL to avoid stack free.
*/
stack_free(info->sigstack.ss_sp + info->sigstack.ss_size, info->sigstack.ss_size);
}
#endif
#ifdef DEBUG
/* for non-debug we do fast exit path and don't free local heap */
HEAP_TYPE_FREE(dcontext, info, thread_sig_info_t, ACCT_OTHER, PROTECTED);
#endif
#ifdef PAPI
/* use SIGPROF for updating gui so it can be distinguished from SIGVTALRM */
set_itimer_callback(
dcontext, ITIMER_PROF, 500,
(void (*func)(dcontext_t *, priv_mcontext_t *))perfctr_update_gui());
#endif
}
void
set_handler_sigact(kernel_sigaction_t *act, int sig, handler_t handler)
{
act->handler = handler;
#ifdef MACOS
/* This is the real target */
act->tramp = (tramp_t)handler;
#endif
act->flags = SA_SIGINFO; /* send 3 args to handler */
#ifdef HAVE_SIGALTSTACK
act->flags |= SA_ONSTACK; /* use our sigstack */
#endif
/* We want the kernel to help us auto-restart syscalls, esp. when our signals
* interrupt native code such as during attach or in client or DR code (i#2659).
*/
act->flags |= SA_RESTART;
#if !defined(VMX86_SERVER) && defined(LINUX)
/* PR 305020: must have SA_RESTORER for x64 */
/* i#2812: must have SA_RESTORER to handle vsyscall32 being disabled */
act->flags |= SA_RESTORER;
act->restorer = (void (*)(void))dynamorio_sigreturn;
#endif
/* We block most signals within our handler */
kernel_sigfillset(&act->mask);
/* i#184/PR 450670: we let our suspend signal interrupt our own handler
* We never send more than one before resuming, so no danger to stack usage
* from our own: but app could pile them up.
*/
kernel_sigdelset(&act->mask, SUSPEND_SIGNAL);
/* i#193/PR 287309: we need to NOT suppress further SIGSEGV, for decode faults,
* for try/except, and for !HAVE_MEMINFO probes.
* Just like SUSPEND_SIGNAL, if app sends repeated SEGV, could run out of
* alt stack: seems too corner-case to be worth increasing stack size.
*/
kernel_sigdelset(&act->mask, SIGSEGV);
if (sig == SUSPEND_SIGNAL || sig == SIGSEGV)
act->flags |= SA_NODEFER;
/* Sigset is a 1 or 2 elt array of longs on X64/X86. Treat as 2 elt of
* uint32. */
IF_DEBUG(uint32 *mask_sig = (uint32 *)&act->mask.sig[0]);
LOG(THREAD_GET, LOG_ASYNCH, 3, "mask for our handler is " PFX " " PFX "\n",
mask_sig[0], mask_sig[1]);
}
static void
set_our_handler_sigact(kernel_sigaction_t *act, int sig)
{
set_handler_sigact(act, sig, (handler_t)master_signal_handler);
}
static void
set_handler_and_record_app(dcontext_t *dcontext, thread_sig_info_t *info, int sig,
kernel_sigaction_t *act)
{
int rc;
kernel_sigaction_t oldact;
ASSERT(sig <= MAX_SIGNUM);
/* arm the signal */
rc = sigaction_syscall(sig, act, &oldact);
ASSERT(rc ==
0
/* Workaround for PR 223720, which was fixed in ESX4.0 but
* is present in ESX3.5 and earlier: vmkernel treats
* 63 and 64 as invalid signal numbers.
*/
IF_VMX86(|| (sig >= 63 && rc == -EINVAL)));
if (rc != 0) /* be defensive: app will probably still work */
return;
if (oldact.handler != (handler_t)SIG_DFL &&
oldact.handler != (handler_t)master_signal_handler) {
/* save the app's action for sig */
if (info->shared_app_sigaction) {
/* app_sigaction structure is shared */
d_r_mutex_lock(info->shared_lock);
}
if (info->app_sigaction[sig] != NULL) {
/* go ahead and toss the old one, it's up to the app to store
* and then restore later if it wants to
*/
handler_free(dcontext, info->app_sigaction[sig], sizeof(kernel_sigaction_t));
}
info->app_sigaction[sig] =
(kernel_sigaction_t *)handler_alloc(dcontext, sizeof(kernel_sigaction_t));
memcpy(info->app_sigaction[sig], &oldact, sizeof(kernel_sigaction_t));
/* clear cache */
info->restorer_valid[sig] = -1;
if (info->shared_app_sigaction)
d_r_mutex_unlock(info->shared_lock);
#ifdef DEBUG
if (oldact.handler == (handler_t)SIG_IGN) {
LOG(THREAD, LOG_ASYNCH, 2,
"app already installed SIG_IGN as sigaction for signal %d\n", sig);
} else {
LOG(THREAD, LOG_ASYNCH, 2,
"app already installed " PFX " as sigaction flags=0x%x for signal %d\n",
oldact.handler, oldact.flags, sig);
}
#endif
} else {
LOG(THREAD, LOG_ASYNCH, 2,
"prior handler is " PFX " vs master " PFX " with flags=0x%x for signal %d\n",
oldact.handler, master_signal_handler, oldact.flags, sig);
if (info->app_sigaction[sig] != NULL) {
if (info->shared_app_sigaction)
d_r_mutex_lock(info->shared_lock);
handler_free(dcontext, info->app_sigaction[sig], sizeof(kernel_sigaction_t));
info->app_sigaction[sig] = NULL;
if (info->shared_app_sigaction)
d_r_mutex_unlock(info->shared_lock);
}
}
LOG(THREAD, LOG_ASYNCH, 3, "\twe intercept signal %d\n", sig);
}
/* Set up master_signal_handler as the handler for signal "sig",
* for the current thread. Since we deal with kernel data structures
* in our interception of system calls, we use them here as well,
* to avoid having to translate to/from libc data structures.
*/
static void
intercept_signal(dcontext_t *dcontext, thread_sig_info_t *info, int sig)
{
kernel_sigaction_t act;
ASSERT(sig <= MAX_SIGNUM);
set_our_handler_sigact(&act, sig);
set_handler_and_record_app(dcontext, info, sig, &act);
}
static void
intercept_signal_ignore_initially(dcontext_t *dcontext, thread_sig_info_t *info, int sig)
{
kernel_sigaction_t act;
ASSERT(sig <= MAX_SIGNUM);
memset(&act, 0, sizeof(act));
act.handler = (handler_t)SIG_IGN;
set_handler_and_record_app(dcontext, info, sig, &act);
}
static void
intercept_signal_no_longer_ignore(dcontext_t *dcontext, thread_sig_info_t *info, int sig)
{
kernel_sigaction_t act;
int rc;
ASSERT(sig <= MAX_SIGNUM);
set_our_handler_sigact(&act, sig);
rc = sigaction_syscall(sig, &act, NULL);
ASSERT(rc == 0);
}
/* i#1921: For proper single-threaded native execution with re-takeover we need
* to propagate signals. For now we only support going completely native in
* this thread but without a full detach, so we abandon our signal handlers w/o
* freeing memory up front.
* We also use this for the start/stop interface where we are going fully native
* for all threads.
*/
void
signal_remove_handlers(dcontext_t *dcontext)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
int i;
kernel_sigaction_t act;
memset(&act, 0, sizeof(act));
act.handler = (handler_t)SIG_DFL;
kernel_sigemptyset(&act.mask);
for (i = 1; i <= MAX_SIGNUM; i++) {
if (info->app_sigaction[i] != NULL) {
LOG(THREAD, LOG_ASYNCH, 2, "\trestoring " PFX " as handler for %d\n",
info->app_sigaction[i]->handler, i);
sigaction_syscall(i, info->app_sigaction[i], NULL);
} else if (info->we_intercept[i]) {
/* restore to default */
LOG(THREAD, LOG_ASYNCH, 2, "\trestoring SIG_DFL as handler for %d\n", i);
sigaction_syscall(i, &act, NULL);
}
}
DODEBUG({ removed_sig_handler = true; });
}
void
signal_remove_alarm_handlers(dcontext_t *dcontext)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
int i;
for (i = 1; i <= MAX_SIGNUM; i++) {
if (!info->we_intercept[i])
continue;
if (sig_is_alarm_signal(i)) {
set_ignore_signal_action(i);
}
}
}
/* For attaching mid-run, we assume regular POSIX with handlers global to just one
* thread group in the process.
* We also use this routine for the initial setup of our handlers, which we
* split from signal_thread_inherit() to support start/stop.
*/
void
signal_reinstate_handlers(dcontext_t *dcontext, bool ignore_alarm)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
int i;
for (i = 1; i <= MAX_SIGNUM; i++) {
bool skip = false;
if (!info->we_intercept[i]) {
skip = true;
if (signal_is_interceptable(i)) {
/* We do have to intercept everything the app does.
* If the app removes its handler, we'll never remove ours, which we
* can live with.
*/
kernel_sigaction_t oldact;
int rc = sigaction_syscall(i, NULL, &oldact);
ASSERT(rc == 0);
if (rc == 0 && oldact.handler != (handler_t)SIG_DFL &&
oldact.handler != (handler_t)master_signal_handler) {
skip = false;
}
}
}
if (skip)
continue;
if (sig_is_alarm_signal(i) && ignore_alarm) {
LOG(THREAD, LOG_ASYNCH, 2, "\tignoring %d initially\n", i);
intercept_signal_ignore_initially(dcontext, info, i);
} else {
LOG(THREAD, LOG_ASYNCH, 2, "\trestoring DR handler for %d\n", i);
intercept_signal(dcontext, info, i);
}
}
DODEBUG({ removed_sig_handler = false; });
}
void
signal_reinstate_alarm_handlers(dcontext_t *dcontext)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
int i;
for (i = 1; i <= MAX_SIGNUM; i++) {
if (!info->we_intercept[i] || !sig_is_alarm_signal(i))
continue;
LOG(THREAD, LOG_ASYNCH, 2, "\trestoring DR handler for %d\n", i);
intercept_signal_no_longer_ignore(dcontext, info, i);
}
}
/**** system call handlers ***********************************************/
/* FIXME: invalid pointer passed to kernel will currently show up
* probably as a segfault in our handlers below...need to make them
* look like kernel, and pass error code back to os.c
*/
void
handle_clone(dcontext_t *dcontext, uint flags)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
if ((flags & CLONE_VM) == 0) {
/* separate process not sharing memory */
if ((flags & CLONE_SIGHAND) != 0) {
/* FIXME: how deal with this?
* "man clone" says: "Since Linux 2.6.0-test6, flags must also
* include CLONE_VM if CLONE_SIGHAND is specified"
*/
LOG(THREAD, LOG_ASYNCH, 1, "WARNING: !CLONE_VM but CLONE_SIGHAND!\n");
ASSERT_NOT_IMPLEMENTED(false);
}
return;
}
pre_second_thread();
if ((flags & CLONE_SIGHAND) != 0) {
/* need to share table of handlers! */
LOG(THREAD, LOG_ASYNCH, 2, "handle_clone: CLONE_SIGHAND set!\n");
if (!info->shared_app_sigaction) {
/* this is the start of a chain of sharing
* no synch needed here, child not created yet
*/
info->shared_app_sigaction = true;
info->shared_refcount =
(int *)global_heap_alloc(sizeof(int) HEAPACCT(ACCT_OTHER));
*info->shared_refcount = 1;
info->shared_lock =
(mutex_t *)global_heap_alloc(sizeof(mutex_t) HEAPACCT(ACCT_OTHER));
ASSIGN_INIT_LOCK_FREE(*info->shared_lock, shared_lock);
} /* else, some ancestor is already owner */
} else {
/* child will inherit copy of current table -> cannot modify it
* until child is scheduled! FIXME: any other way?
*/
d_r_mutex_lock(&info->child_lock);
info->num_unstarted_children++;
d_r_mutex_unlock(&info->child_lock);
}
if (TEST(CLONE_THREAD, flags) && os_itimers_thread_shared()) {
if (!info->shared_itimer) {
/* this is the start of a chain of sharing
* no synch needed here, child not created yet
*/
info->shared_itimer = true;
info->shared_itimer_refcount =
(int *)global_heap_alloc(sizeof(int) HEAPACCT(ACCT_OTHER));
*info->shared_itimer_refcount = 1;
info->shared_itimer_underDR =
(int *)global_heap_alloc(sizeof(int) HEAPACCT(ACCT_OTHER));
*info->shared_itimer_underDR = 1;
} /* else, some ancestor already created */
}
}
/* Returns false if should NOT issue syscall.
* In such a case, the result is in "result".
* If *result is non-zero, the syscall should fail.
* We could instead issue the syscall and expect it to fail, which would have a more
* accurate error code, but that risks missing a failure (e.g., RT on Android
* which in some cases returns success on bugus params).
* It seems better to err on the side of the wrong error code or failing when
* we shouldn't, than to think it failed when it didn't, which is more complex
* to deal with.
*/
bool
handle_sigaction(dcontext_t *dcontext, int sig, const kernel_sigaction_t *act,
prev_sigaction_t *oact, size_t sigsetsize, OUT uint *result)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
kernel_sigaction_t *save;
kernel_sigaction_t local_act;
if (sigsetsize != sizeof(kernel_sigset_t)) {
*result = EINVAL;
return false;
}
if (act != NULL) {
/* Linux checks readability before checking the signal number. */
if (!d_r_safe_read(act, sizeof(local_act), &local_act)) {
*result = EFAULT;
return false;
}
}
/* i#1135: app may pass invalid signum to find MAX_SIGNUM */
if (sig <= 0 || sig > MAX_SIGNUM || (act != NULL && !signal_is_interceptable(sig))) {
*result = EINVAL;
return false;
}
if (act != NULL) {
/* app is installing a new action */
while (info->num_unstarted_children > 0) {
/* must wait for children to start and copy our state
* before we modify it!
*/
os_thread_yield();
}
info->sigaction_param = act;
}
if (info->shared_app_sigaction) {
/* app_sigaction structure is shared */
d_r_mutex_lock(info->shared_lock);
}
if (oact != NULL) {
/* Keep a copy of the prior one for post-syscall to hand to the app. */
info->use_kernel_prior_sigaction = false;
if (info->app_sigaction[sig] == NULL) {
if (info->we_intercept[sig]) {
/* need to pretend there is no handler */
memset(&info->prior_app_sigaction, 0, sizeof(info->prior_app_sigaction));
info->prior_app_sigaction.handler = (handler_t)SIG_DFL;
} else {
info->use_kernel_prior_sigaction = true;
}
} else {
memcpy(&info->prior_app_sigaction, info->app_sigaction[sig],
sizeof(info->prior_app_sigaction));
}
}
if (act != NULL) {
if (local_act.handler == (handler_t)SIG_IGN ||
local_act.handler == (handler_t)SIG_DFL) {
LOG(THREAD, LOG_ASYNCH, 2, "app installed %s as sigaction for signal %d\n",
(local_act.handler == (handler_t)SIG_IGN) ? "SIG_IGN" : "SIG_DFL", sig);
if (!info->we_intercept[sig]) {
/* let the SIG_IGN/SIG_DFL go through, we want to remove our
* handler. we delete the stored app_sigaction in post_
*/
if (info->shared_app_sigaction)
d_r_mutex_unlock(info->shared_lock);
return true;
}
} else {
LOG(THREAD, LOG_ASYNCH, 2,
"app installed " PFX " as sigaction for signal %d\n", local_act.handler,
sig);
DOLOG(2, LOG_ASYNCH, {
LOG(THREAD, LOG_ASYNCH, 2, "signal mask for handler:\n");
dump_sigset(dcontext, (kernel_sigset_t *)&local_act.mask);
});
}
/* save app's entire sigaction struct */
save = (kernel_sigaction_t *)handler_alloc(dcontext, sizeof(kernel_sigaction_t));
memcpy(save, &local_act, sizeof(kernel_sigaction_t));
/* Remove the unblockable sigs */
kernel_sigdelset(&save->mask, SIGKILL);
kernel_sigdelset(&save->mask, SIGSTOP);
#ifdef X86
/* Remove flags not allowed to be passed to the kernel (this also zeroes
* the top 32 bits, like the kernel does: i#3681).
*/
save->flags &= ~(SA_IA32_ABI | SA_X32_ABI);
#endif
if (info->app_sigaction[sig] != NULL) {
/* go ahead and toss the old one, it's up to the app to store
* and then restore later if it wants to
*/
handler_free(dcontext, info->app_sigaction[sig], sizeof(kernel_sigaction_t));
}
info->app_sigaction[sig] = save;
LOG(THREAD, LOG_ASYNCH, 3, "\tflags = " PFX ", %s = " PFX "\n", local_act.flags,
IF_MACOS_ELSE("tramp", "restorer"),
IF_MACOS_ELSE(local_act.tramp, local_act.restorer));
/* clear cache */
info->restorer_valid[sig] = -1;
}
if (info->shared_app_sigaction)
d_r_mutex_unlock(info->shared_lock);
if (info->we_intercept[sig]) {
/* cancel the syscall */
*result = handle_post_sigaction(dcontext, true, sig, act, oact, sigsetsize);
return false;
}
if (act != NULL) {
/* Now hand kernel our master handler instead of app's. */
set_our_handler_sigact(&info->our_sigaction, sig);
set_syscall_param(dcontext, 1, (reg_t)&info->our_sigaction);
/* FIXME PR 297033: we don't support intercepting DEFAULT_STOP /
* DEFAULT_CONTINUE signals b/c we can't generate the default
* action: if the app registers a handler, though, we should work
* properly if we never see SIG_DFL.
*/
}
return true;
}
/* os.c thinks it's passing us struct_sigaction, really it's kernel_sigaction_t,
* which has fields in different order.
* Only called on success.
* Returns the desired app return value (caller will negate if nec).
*/
uint
handle_post_sigaction(dcontext_t *dcontext, bool success, int sig,
const kernel_sigaction_t *act, prev_sigaction_t *oact,
size_t sigsetsize)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
if (act != NULL) {
/* Restore app register value, in case we changed it. */
set_syscall_param(dcontext, 1, (reg_t)info->sigaction_param);
}
if (!success)
return 0; /* don't change return value */
ASSERT(sig <= MAX_SIGNUM && sig > 0);
if (oact != NULL) {
if (info->use_kernel_prior_sigaction) {
/* Real syscall succeeded with oact so it must be readable, barring races. */
ASSERT(oact->handler == (handler_t)SIG_IGN ||
oact->handler == (handler_t)SIG_DFL);
} else {
/* We may have skipped the syscall so we have to check writability */
#ifdef MACOS
/* On MacOS prev_sigaction_t is a different type (i#2105) */
bool fault = true;
TRY_EXCEPT(dcontext,
{
oact->handler = info->prior_app_sigaction.handler;
oact->mask = info->prior_app_sigaction.mask;
oact->flags = info->prior_app_sigaction.flags;
fault = false;
},
{
/* EXCEPT */
/* nothing: fault is already true */
});
if (fault)
return EFAULT;
#else
if (!safe_write_ex(oact, sizeof(*oact), &info->prior_app_sigaction, NULL)) {
/* We actually don't have to undo installing any passed action
* b/c the Linux kernel does that *before* checking oact perms.
*/
return EFAULT;
}
#endif
}
}
/* If installing IGN or DFL, delete ours.
* XXX: This is racy. We can't hold the lock across the syscall, though.
* What we should do is just drop support for -no_intercept_all_signals,
* which is off by default anyway and never turned off.
*/
if (act != NULL &&
/* De-ref here should work barring races: already racy and non-default so not
* bothering with safe_read.
*/
((act->handler == (handler_t)SIG_IGN || act->handler == (handler_t)SIG_DFL) &&
!info->we_intercept[sig]) &&
info->app_sigaction[sig] != NULL) {
if (info->shared_app_sigaction)
d_r_mutex_lock(info->shared_lock);
/* remove old stored app action */
handler_free(dcontext, info->app_sigaction[sig], sizeof(kernel_sigaction_t));
info->app_sigaction[sig] = NULL;
if (info->shared_app_sigaction)
d_r_mutex_unlock(info->shared_lock);
}
return 0;
}
#ifdef LINUX
static bool
convert_old_sigaction_to_kernel(dcontext_t *dcontext, kernel_sigaction_t *ks,
const old_sigaction_t *os)
{
bool res = false;
TRY_EXCEPT(dcontext,
{
ks->handler = os->handler;
ks->flags = os->flags;
ks->restorer = os->restorer;
kernel_sigemptyset(&ks->mask);
ks->mask.sig[0] = os->mask;
res = true;
},
{
/* EXCEPT */
/* nothing: res is already false */
});
return res;
}
static bool
convert_kernel_sigaction_to_old(dcontext_t *dcontext, old_sigaction_t *os,
const kernel_sigaction_t *ks)
{
bool res = false;
TRY_EXCEPT(dcontext,
{
os->handler = ks->handler;
os->flags = ks->flags;
os->restorer = ks->restorer;
os->mask = ks->mask.sig[0];
res = true;
},
{
/* EXCEPT */
/* nothing: res is already false */
});
return res;
}
/* Returns false (and "result") if should NOT issue syscall. */
bool
handle_old_sigaction(dcontext_t *dcontext, int sig, const old_sigaction_t *act,
old_sigaction_t *oact, OUT uint *result)
{
kernel_sigaction_t kact;
kernel_sigaction_t okact;
bool res;
if (act != NULL) {
if (!convert_old_sigaction_to_kernel(dcontext, &kact, act)) {
*result = EFAULT;
return false;
}
}
res = handle_sigaction(dcontext, sig, act == NULL ? NULL : &kact,
oact == NULL ? NULL : &okact, sizeof(kernel_sigset_t), result);
if (!res)
*result = handle_post_old_sigaction(dcontext, true, sig, act, oact);
return res;
}
/* Returns the desired app return value (caller will negate if nec). */
uint
handle_post_old_sigaction(dcontext_t *dcontext, bool success, int sig,
const old_sigaction_t *act, old_sigaction_t *oact)
{
kernel_sigaction_t kact;
kernel_sigaction_t okact;
ptr_uint_t res;
if (act != NULL && success) {
if (!convert_old_sigaction_to_kernel(dcontext, &kact, act)) {
ASSERT(!success);
return EFAULT;
}
}
if (oact != NULL && success) {
if (!convert_old_sigaction_to_kernel(dcontext, &okact, oact)) {
ASSERT(!success);
return EFAULT;
}
}
res = handle_post_sigaction(dcontext, success, sig, act == NULL ? NULL : &kact,
oact == NULL ? NULL : &okact, sizeof(kernel_sigset_t));
if (res == 0 && oact != NULL) {
if (!convert_kernel_sigaction_to_old(dcontext, oact, &okact)) {
return EFAULT;
}
}
return res;
}
#endif /* LINUX */
/* Returns false and sets *result if should NOT issue syscall.
* If *result is non-zero, the syscall should fail.
*/
bool
handle_sigaltstack(dcontext_t *dcontext, const stack_t *stack, stack_t *old_stack,
reg_t cur_xsp, OUT uint *result)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
stack_t local_stack;
if (old_stack != NULL) {
if (!safe_write_ex(old_stack, sizeof(*old_stack), &info->app_sigstack, NULL)) {
*result = EFAULT;
return false;
}
}
if (stack != NULL) {
/* Fail in the same way the kernel does. */
if (!d_r_safe_read(stack, sizeof(local_stack), &local_stack)) {
*result = EFAULT;
return false;
}
if (APP_HAS_SIGSTACK(info)) {
/* The app is not allowed to set a new altstack while on the current one. */
reg_t cur_sigstk = (reg_t)info->app_sigstack.ss_sp;
if (cur_xsp >= cur_sigstk &&
cur_xsp < cur_sigstk + info->app_sigstack.ss_size) {
*result = EPERM;
return false;
}
}
uint key_flag = local_stack.ss_flags & ~SS_FLAG_BITS;
if (key_flag != SS_DISABLE && key_flag != SS_ONSTACK && key_flag != 0) {
*result = EINVAL;
return false;
}
if (key_flag == SS_DISABLE) {
/* Zero the other params and don't even check them. */
local_stack.ss_sp = NULL;
local_stack.ss_size = 0;
} else {
if (local_stack.ss_size < MINSIGSTKSZ) {
*result = ENOMEM;
return false;
}
}
info->app_sigstack = local_stack;
LOG(THREAD, LOG_ASYNCH, 2, "Setting app signal stack to " PFX "-" PFX " %d=%s\n",
local_stack.ss_sp, local_stack.ss_sp + local_stack.ss_size - 1,
local_stack.ss_flags, (APP_HAS_SIGSTACK(info)) ? "enabled" : "disabled");
}
*result = 0;
return false; /* always cancel syscall */
}
/* Blocked signals:
* In general, we don't need to keep track of blocked signals.
* We only need to do so for those signals we intercept ourselves.
* Thus, info->app_sigblocked ONLY contains entries for signals
* we intercept ourselves.
* PR 304708: we now intercept all signals.
*/
static void
set_blocked(dcontext_t *dcontext, kernel_sigset_t *set, bool absolute)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
int i;
if (absolute) {
/* discard current blocked signals, re-set from new mask */
kernel_sigemptyset(&info->app_sigblocked);
} /* else, OR in the new set */
for (i = 1; i <= MAX_SIGNUM; i++) {
if (EMULATE_SIGMASK(info, i) && kernel_sigismember(set, i)) {
kernel_sigaddset(&info->app_sigblocked, i);
}
}
#ifdef DEBUG
if (d_r_stats->loglevel >= 3 && (d_r_stats->logmask & LOG_ASYNCH) != 0) {
LOG(THREAD, LOG_ASYNCH, 3, "blocked signals are now:\n");
dump_sigset(dcontext, &info->app_sigblocked);
}
#endif
}
void
signal_set_mask(dcontext_t *dcontext, kernel_sigset_t *sigset)
{
set_blocked(dcontext, sigset, true /*absolute*/);
}
void
signal_swap_mask(dcontext_t *dcontext, bool to_app)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
if (to_app) {
if (init_info.app_sigaction != NULL) {
/* This is the first execution of the app.
* We need to remove our own init-time handler and mask.
*/
unset_initial_crash_handlers(dcontext);
return;
}
sigprocmask_syscall(SIG_SETMASK, &info->app_sigblocked, NULL,
sizeof(info->app_sigblocked));
} else {
unblock_all_signals(&info->app_sigblocked);
DOLOG(2, LOG_ASYNCH, {
LOG(THREAD, LOG_ASYNCH, 2, "thread %d's initial app signal mask:\n",
d_r_get_thread_id());
dump_sigset(dcontext, &info->app_sigblocked);
});
}
}
/* Scans over info->sigpending to see if there are any unblocked, pending
* signals, and sets dcontext->signals_pending if there are. Do this after
* modifying the set of signals blocked by the application.
*/
void
check_signals_pending(dcontext_t *dcontext, thread_sig_info_t *info)
{
int i;
if (dcontext->signals_pending != 0)
return;
for (i = 1; i <= MAX_SIGNUM; i++) {
if (info->sigpending[i] != NULL &&
!kernel_sigismember(&info->app_sigblocked, i) && !dcontext->signals_pending) {
/* We only update the application's set of blocked signals from
* syscall handlers, so we know we'll go back to d_r_dispatch and see
* this flag right away.
*/
LOG(THREAD, LOG_ASYNCH, 3, "\tsetting signals_pending flag\n");
dcontext->signals_pending = 1;
break;
}
}
}
/* Returns whether to execute the syscall */
bool
handle_sigprocmask(dcontext_t *dcontext, int how, kernel_sigset_t *app_set,
kernel_sigset_t *oset, size_t sigsetsize)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
int i;
kernel_sigset_t safe_set;
/* If we're intercepting all, we emulate the whole thing */
bool execute_syscall = !DYNAMO_OPTION(intercept_all_signals);
LOG(THREAD, LOG_ASYNCH, 2, "handle_sigprocmask\n");
if (oset != NULL)
info->pre_syscall_app_sigblocked = info->app_sigblocked;
if (app_set != NULL && d_r_safe_read(app_set, sizeof(safe_set), &safe_set)) {
if (execute_syscall) {
/* The syscall will execute, so remove from the set passed
* to it. We restore post-syscall.
* XXX i#1187: we could crash here touching app memory -- could
* use TRY, but the app could pass read-only memory and it
* would work natively! Better to swap in our own
* allocated data struct. There's a transparency issue w/
* races too if another thread looks at this memory. This
* won't happen by default b/c -intercept_all_signals is
* on by default so we don't try to solve all these
* issues.
*/
info->pre_syscall_app_sigprocmask = safe_set;
}
if (how == SIG_BLOCK) {
/* The set of blocked signals is the union of the current
* set and the set argument.
*/
for (i = 1; i <= MAX_SIGNUM; i++) {
if (EMULATE_SIGMASK(info, i) && kernel_sigismember(&safe_set, i)) {
kernel_sigaddset(&info->app_sigblocked, i);
if (execute_syscall)
kernel_sigdelset(app_set, i);
}
}
} else if (how == SIG_UNBLOCK) {
/* The signals in set are removed from the current set of
* blocked signals.
*/
for (i = 1; i <= MAX_SIGNUM; i++) {
if (EMULATE_SIGMASK(info, i) && kernel_sigismember(&safe_set, i)) {
kernel_sigdelset(&info->app_sigblocked, i);
if (execute_syscall)
kernel_sigdelset(app_set, i);
}
}
} else if (how == SIG_SETMASK) {
/* The set of blocked signals is set to the argument set. */
kernel_sigemptyset(&info->app_sigblocked);
for (i = 1; i <= MAX_SIGNUM; i++) {
if (EMULATE_SIGMASK(info, i) && kernel_sigismember(&safe_set, i)) {
kernel_sigaddset(&info->app_sigblocked, i);
if (execute_syscall)
kernel_sigdelset(app_set, i);
}
}
}
#ifdef DEBUG
if (d_r_stats->loglevel >= 3 && (d_r_stats->logmask & LOG_ASYNCH) != 0) {
LOG(THREAD, LOG_ASYNCH, 3, "blocked signals are now:\n");
dump_sigset(dcontext, &info->app_sigblocked);
}
#endif
/* make sure we deliver pending signals that are now unblocked
* FIXME: consider signal #S, which we intercept ourselves.
* If S arrives, then app blocks it prior to our delivering it,
* we then won't deliver it until app unblocks it...is this a
* problem? Could have arrived a little later and then we would
* do same thing, but this way kernel may send one more than would
* get w/o dynamo? This goes away if we deliver signals
* prior to letting app do a syscall.
*/
check_signals_pending(dcontext, info);
}
if (!execute_syscall) {
handle_post_sigprocmask(dcontext, how, app_set, oset, sigsetsize);
return false; /* skip syscall */
} else
return true;
}
/* need to add in our signals that the app thinks are blocked */
void
handle_post_sigprocmask(dcontext_t *dcontext, int how, kernel_sigset_t *app_set,
kernel_sigset_t *oset, size_t sigsetsize)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
int i;
if (!DYNAMO_OPTION(intercept_all_signals)) {
/* Restore app memory */
safe_write_ex(app_set, sizeof(*app_set), &info->pre_syscall_app_sigprocmask,
NULL);
}
if (oset != NULL) {
if (DYNAMO_OPTION(intercept_all_signals))
safe_write_ex(oset, sizeof(*oset), &info->pre_syscall_app_sigblocked, NULL);
else {
/* the syscall wrote to oset already, so just add any additional */
for (i = 1; i <= MAX_SIGNUM; i++) {
if (EMULATE_SIGMASK(info, i) &&
/* use the pre-syscall value: do not take into account changes
* from this syscall itself! (PR 523394)
*/
kernel_sigismember(&info->pre_syscall_app_sigblocked, i)) {
kernel_sigaddset(oset, i);
}
}
}
}
}
void
handle_sigsuspend(dcontext_t *dcontext, kernel_sigset_t *set, size_t sigsetsize)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
int i;
ASSERT(set != NULL);
LOG(THREAD, LOG_ASYNCH, 2, "handle_sigsuspend\n");
info->in_sigsuspend = true;
info->app_sigblocked_save = info->app_sigblocked;
kernel_sigemptyset(&info->app_sigblocked);
for (i = 1; i <= MAX_SIGNUM; i++) {
if (EMULATE_SIGMASK(info, i) && kernel_sigismember(set, i)) {
kernel_sigaddset(&info->app_sigblocked, i);
kernel_sigdelset(set, i);
}
}
#ifdef DEBUG
if (d_r_stats->loglevel >= 3 && (d_r_stats->logmask & LOG_ASYNCH) != 0) {
LOG(THREAD, LOG_ASYNCH, 3, "in sigsuspend, blocked signals are now:\n");
dump_sigset(dcontext, &info->app_sigblocked);
}
#endif
}
/**** utility routines ***********************************************/
#ifdef DEBUG
static void
dump_sigset(dcontext_t *dcontext, kernel_sigset_t *set)
{
int sig;
for (sig = 1; sig <= MAX_SIGNUM; sig++) {
if (kernel_sigismember(set, sig))
LOG(THREAD, LOG_ASYNCH, 1, "\t%d = blocked\n", sig);
}
}
#endif /* DEBUG */
/* PR 205795: to avoid lock problems w/ in_fcache (it grabs a lock, we
* could have interrupted someone holding that), we first check
* whereami --- if whereami is DR_WHERE_FCACHE we still check the pc
* to distinguish generated routines, but at least we're certain
* it's not in DR where it could own a lock.
* We can't use is_on_dstack() here b/c we need to handle clean call
* arg crashes -- which is too bad since checking client dll and DR dll is
* not sufficient due to calls to ntdll, libc, or pc being in gencode.
*/
static bool
safe_is_in_fcache(dcontext_t *dcontext, app_pc pc, app_pc xsp)
{
if (dcontext->whereami != DR_WHERE_FCACHE ||
IF_CLIENT_INTERFACE(is_in_client_lib(pc) ||) is_in_dynamo_dll(pc) ||
is_on_initstack(xsp))
return false;
/* Reasonably certain not in DR code, so no locks should be held */
return in_fcache(pc);
}
static bool
safe_is_in_coarse_stubs(dcontext_t *dcontext, app_pc pc, app_pc xsp)
{
if (dcontext->whereami != DR_WHERE_FCACHE ||
IF_CLIENT_INTERFACE(is_in_client_lib(pc) ||) is_in_dynamo_dll(pc) ||
is_on_initstack(xsp))
return false;
/* Reasonably certain not in DR code, so no locks should be held */
return in_coarse_stubs(pc);
}
static bool
is_on_alt_stack(dcontext_t *dcontext, byte *sp)
{
#ifdef HAVE_SIGALTSTACK
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
return (sp >= (byte *)info->sigstack.ss_sp &&
/* deliberate equality check since stacks often init to top */
sp <= (byte *)(info->sigstack.ss_sp + info->sigstack.ss_size));
#else
return false;
#endif
}
/* The caller must initialize ucxt, including its fpstate pointer for x86 Linux. */
static void
sig_full_initialize(sig_full_cxt_t *sc_full, kernel_ucontext_t *ucxt)
{
sc_full->sc = SIGCXT_FROM_UCXT(ucxt);
#ifdef X86
sc_full->fp_simd_state = NULL; /* we have a ptr inside sigcontext_t */
#elif defined(ARM)
sc_full->fp_simd_state = &ucxt->coproc.uc_vfp;
#elif defined(AARCH64)
sc_full->fp_simd_state = &ucxt->uc_mcontext.__reserved;
#else
ASSERT_NOT_IMPLEMENTED(false);
#endif
}
void
sigcontext_to_mcontext(priv_mcontext_t *mc, sig_full_cxt_t *sc_full,
dr_mcontext_flags_t flags)
{
sigcontext_t *sc = sc_full->sc;
ASSERT(mc != NULL && sc != NULL);
#ifdef X86
if (TEST(DR_MC_INTEGER, flags)) {
mc->xax = sc->SC_XAX;
mc->xbx = sc->SC_XBX;
mc->xcx = sc->SC_XCX;
mc->xdx = sc->SC_XDX;
mc->xsi = sc->SC_XSI;
mc->xdi = sc->SC_XDI;
mc->xbp = sc->SC_XBP;
# ifdef X64
mc->r8 = sc->SC_FIELD(r8);
mc->r9 = sc->SC_FIELD(r9);
mc->r10 = sc->SC_FIELD(r10);
mc->r11 = sc->SC_FIELD(r11);
mc->r12 = sc->SC_FIELD(r12);
mc->r13 = sc->SC_FIELD(r13);
mc->r14 = sc->SC_FIELD(r14);
mc->r15 = sc->SC_FIELD(r15);
# endif /* X64 */
}
if (TEST(DR_MC_CONTROL, flags)) {
mc->xsp = sc->SC_XSP;
mc->xflags = sc->SC_XFLAGS;
mc->pc = (app_pc)sc->SC_XIP;
}
#elif defined(AARCH64)
if (TEST(DR_MC_INTEGER, flags))
memcpy(&mc->r0, &sc->SC_FIELD(regs[0]), sizeof(mc->r0) * 31);
if (TEST(DR_MC_CONTROL, flags)) {
/* XXX i#2710: the link register should be under DR_MC_CONTROL */
mc->sp = sc->SC_FIELD(sp);
mc->pc = (void *)sc->SC_FIELD(pc);
mc->nzcv = sc->SC_FIELD(pstate);
}
#elif defined(ARM)
if (TEST(DR_MC_INTEGER, flags)) {
mc->r0 = sc->SC_FIELD(arm_r0);
mc->r1 = sc->SC_FIELD(arm_r1);
mc->r2 = sc->SC_FIELD(arm_r2);
mc->r3 = sc->SC_FIELD(arm_r3);
mc->r4 = sc->SC_FIELD(arm_r4);
mc->r5 = sc->SC_FIELD(arm_r5);
mc->r6 = sc->SC_FIELD(arm_r6);
mc->r7 = sc->SC_FIELD(arm_r7);
mc->r8 = sc->SC_FIELD(arm_r8);
mc->r9 = sc->SC_FIELD(arm_r9);
mc->r10 = sc->SC_FIELD(arm_r10);
mc->r11 = sc->SC_FIELD(arm_fp);
mc->r12 = sc->SC_FIELD(arm_ip);
/* XXX i#2710: the link register should be under DR_MC_CONTROL */
mc->r14 = sc->SC_FIELD(arm_lr);
}
if (TEST(DR_MC_CONTROL, flags)) {
mc->r13 = sc->SC_FIELD(arm_sp);
mc->r15 = sc->SC_FIELD(arm_pc);
mc->cpsr = sc->SC_FIELD(arm_cpsr);
}
# ifdef X64
# error NYI on AArch64
# endif /* X64 */
#endif /* X86/ARM */
if (TEST(DR_MC_MULTIMEDIA, flags))
sigcontext_to_mcontext_simd(mc, sc_full);
}
/* Note that unlike mcontext_to_context(), this routine does not fill in
* any state that is not present in the mcontext: in particular, it assumes
* the sigcontext already contains the native fpstate. If the caller
* is generating a synthetic sigcontext, the caller should call
* save_fpstate() before calling this routine.
*/
/* XXX: on ARM, sigreturn needs the T bit set in the sigcontext_t cpsr field in
* order to return to Thumb mode. But, our mcontext doesn't have the T bit (b/c
* usermode can't read it). Thus callers must either modify an mcontext
* obtained from sigcontext_to_mcontext() or must call set_pc_mode_in_cpsr() in
* order to create a proper sigcontext for sigreturn. All callers here do so.
* The only external non-Windows caller of thread_set_mcontext() is
* translate_from_synchall_to_dispatch() who first does a thread_get_mcontext();
* thread_get_mcontext() sets the dcontext isa_mode from cpsr, and thread_set_mcontext()
* sets it.
*/
void
mcontext_to_sigcontext(sig_full_cxt_t *sc_full, priv_mcontext_t *mc,
dr_mcontext_flags_t flags)
{
sigcontext_t *sc = sc_full->sc;
ASSERT(mc != NULL && sc != NULL);
#ifdef X86
if (TEST(DR_MC_INTEGER, flags)) {
sc->SC_XAX = mc->xax;
sc->SC_XBX = mc->xbx;
sc->SC_XCX = mc->xcx;
sc->SC_XDX = mc->xdx;
sc->SC_XSI = mc->xsi;
sc->SC_XDI = mc->xdi;
sc->SC_XBP = mc->xbp;
# ifdef X64
sc->SC_FIELD(r8) = mc->r8;
sc->SC_FIELD(r9) = mc->r9;
sc->SC_FIELD(r10) = mc->r10;
sc->SC_FIELD(r11) = mc->r11;
sc->SC_FIELD(r12) = mc->r12;
sc->SC_FIELD(r13) = mc->r13;
sc->SC_FIELD(r14) = mc->r14;
sc->SC_FIELD(r15) = mc->r15;
# endif /* X64 */
}
if (TEST(DR_MC_CONTROL, flags)) {
sc->SC_XSP = mc->xsp;
sc->SC_XFLAGS = mc->xflags;
sc->SC_XIP = (ptr_uint_t)mc->pc;
}
#elif defined(AARCH64)
if (TEST(DR_MC_INTEGER, flags)) {
memcpy(&sc->SC_FIELD(regs[0]), &mc->r0, sizeof(mc->r0) * 31);
}
if (TEST(DR_MC_CONTROL, flags)) {
/* XXX i#2710: the link register should be under DR_MC_CONTROL */
sc->SC_FIELD(sp) = mc->sp;
sc->SC_FIELD(pc) = (ptr_uint_t)mc->pc;
sc->SC_FIELD(pstate) = mc->nzcv;
}
#elif defined(ARM)
if (TEST(DR_MC_INTEGER, flags)) {
sc->SC_FIELD(arm_r0) = mc->r0;
sc->SC_FIELD(arm_r1) = mc->r1;
sc->SC_FIELD(arm_r2) = mc->r2;
sc->SC_FIELD(arm_r3) = mc->r3;
sc->SC_FIELD(arm_r4) = mc->r4;
sc->SC_FIELD(arm_r5) = mc->r5;
sc->SC_FIELD(arm_r6) = mc->r6;
sc->SC_FIELD(arm_r7) = mc->r7;
sc->SC_FIELD(arm_r8) = mc->r8;
sc->SC_FIELD(arm_r9) = mc->r9;
sc->SC_FIELD(arm_r10) = mc->r10;
sc->SC_FIELD(arm_fp) = mc->r11;
sc->SC_FIELD(arm_ip) = mc->r12;
/* XXX i#2710: the link register should be under DR_MC_CONTROL */
sc->SC_FIELD(arm_lr) = mc->r14;
}
if (TEST(DR_MC_CONTROL, flags)) {
sc->SC_FIELD(arm_sp) = mc->r13;
sc->SC_FIELD(arm_pc) = mc->r15;
sc->SC_FIELD(arm_cpsr) = mc->cpsr;
}
# ifdef X64
# error NYI on AArch64
# endif /* X64 */
#endif /* X86/ARM */
if (TEST(DR_MC_MULTIMEDIA, flags))
mcontext_to_sigcontext_simd(sc_full, mc);
}
static void
ucontext_to_mcontext(priv_mcontext_t *mc, kernel_ucontext_t *uc)
{
sig_full_cxt_t sc_full;
sig_full_initialize(&sc_full, uc);
sigcontext_to_mcontext(mc, &sc_full, DR_MC_ALL);
}
static void
mcontext_to_ucontext(kernel_ucontext_t *uc, priv_mcontext_t *mc)
{
sig_full_cxt_t sc_full;
sig_full_initialize(&sc_full, uc);
mcontext_to_sigcontext(&sc_full, mc, DR_MC_ALL);
}
#ifdef AARCHXX
static void
set_sigcxt_stolen_reg(sigcontext_t *sc, reg_t val)
{
*(&sc->SC_R0 + (dr_reg_stolen - DR_REG_R0)) = val;
}
static reg_t
get_sigcxt_stolen_reg(sigcontext_t *sc)
{
return *(&sc->SC_R0 + (dr_reg_stolen - DR_REG_R0));
}
# ifndef AARCH64
static dr_isa_mode_t
get_pc_mode_from_cpsr(sigcontext_t *sc)
{
return TEST(EFLAGS_T, sc->SC_XFLAGS) ? DR_ISA_ARM_THUMB : DR_ISA_ARM_A32;
}
dr_isa_mode_t
get_sigcontext_isa_mode(sig_full_cxt_t *sc_full)
{
return get_pc_mode_from_cpsr(sc_full->sc);
}
static void
set_pc_mode_in_cpsr(sigcontext_t *sc, dr_isa_mode_t isa_mode)
{
if (isa_mode == DR_ISA_ARM_THUMB)
sc->SC_XFLAGS |= EFLAGS_T;
else
sc->SC_XFLAGS &= ~EFLAGS_T;
}
void
set_sigcontext_isa_mode(sig_full_cxt_t *sc_full, dr_isa_mode_t isa_mode)
{
set_pc_mode_in_cpsr(sc_full->sc, isa_mode);
}
# endif
#endif
/* Returns whether successful. If avoid_failure, tries to translate
* at least pc if not successful. Pass f if known.
*/
static bool
translate_sigcontext(dcontext_t *dcontext, kernel_ucontext_t *uc, bool avoid_failure,
fragment_t *f)
{
bool success = false;
priv_mcontext_t mcontext;
sigcontext_t *sc = SIGCXT_FROM_UCXT(uc);
ucontext_to_mcontext(&mcontext, uc);
/* FIXME: if cannot find exact match, we're in trouble!
* probably ok to delay, since that indicates not a synchronous
* signal.
*/
/* FIXME : in_fcache() (called by recreate_app_state) grabs fcache
* fcache_unit_areas.lock, we could deadlock! Also on initexit_lock
* == PR 205795/1317
*/
/* For safe recreation we need to either be couldbelinking or hold the
* initexit lock (to keep someone from flushing current fragment), the
* initexit lock is easier
*/
d_r_mutex_lock(&thread_initexit_lock);
/* PR 214962: we assume we're going to relocate to this stored context,
* so we restore memory now
*/
if (translate_mcontext(dcontext->thread_record, &mcontext, true /*restore memory*/,
f)) {
mcontext_to_ucontext(uc, &mcontext);
success = true;
} else {
if (avoid_failure) {
ASSERT_NOT_REACHED(); /* Raise a visible debug error: sthg is wrong. */
/* FIXME : what to do? reg state might be wrong at least get pc */
if (safe_is_in_fcache(dcontext, (cache_pc)sc->SC_XIP, (app_pc)sc->SC_XSP)) {
sc->SC_XIP = (ptr_uint_t)recreate_app_pc(dcontext, mcontext.pc, f);
ASSERT(sc->SC_XIP != (ptr_uint_t)NULL);
} else {
/* FIXME : can't even get pc right, what do we do here? */
sc->SC_XIP = 0;
}
}
}
d_r_mutex_unlock(&thread_initexit_lock);
/* FIXME i#2095: restore the app's segment register value(s). */
LOG(THREAD, LOG_ASYNCH, 3,
"\ttranslate_sigcontext: just set frame's eip to " PFX "\n", sc->SC_XIP);
return success;
}
/* Takes an os-specific context */
void
thread_set_self_context(void *cxt)
{
#ifdef X86
if (!INTERNAL_OPTION(use_sigreturn_setcontext)) {
sigcontext_t *sc = (sigcontext_t *)cxt;
dr_jmp_buf_t buf;
buf.xbx = sc->SC_XBX;
buf.xcx = sc->SC_XCX;
buf.xdi = sc->SC_XDI;
buf.xsi = sc->SC_XSI;
buf.xbp = sc->SC_XBP;
/* XXX: this is not fully transparent: it assumes the target stack
* is valid and that we can clobber the slot beyond TOS.
* Using this instead of sigreturn is meant mainly as a diagnostic
* to help debug future issues with sigreturn (xref i#2080).
*/
buf.xsp = sc->SC_XSP - XSP_SZ; /* extra slot for retaddr */
buf.xip = sc->SC_XIP;
# ifdef X64
buf.r8 = sc->SC_R8;
buf.r9 = sc->SC_R9;
buf.r10 = sc->SC_R10;
buf.r11 = sc->SC_R11;
buf.r12 = sc->SC_R12;
buf.r13 = sc->SC_R13;
buf.r14 = sc->SC_R14;
buf.r15 = sc->SC_R15;
# endif
dr_longjmp(&buf, sc->SC_XAX);
return;
}
#endif
/* Unlike Windows we can't say "only set this subset of the
* full machine state", so we need to get the rest of the state,
*/
sigframe_rt_t frame;
#if defined(LINUX) || defined(DEBUG)
sigcontext_t *sc = (sigcontext_t *)cxt;
#endif
app_pc xsp_for_sigreturn;
#ifdef VMX86_SERVER
ASSERT_NOT_IMPLEMENTED(false); /* PR 405694: can't use regular sigreturn! */
#endif
memset(&frame, 0, sizeof(frame));
#if defined(X86)
dcontext_t *dcontext = get_thread_private_dcontext();
#endif
#ifdef LINUX
# ifdef X86
byte *xstate = get_and_initialize_xstate_buffer(dcontext);
frame.uc.uc_mcontext.fpstate = &((kernel_xstate_t *)xstate)->fpstate;
# endif /* X86 */
frame.uc.uc_mcontext = *sc;
#endif
IF_ARM(ASSERT_NOT_TESTED());
#if defined(X86)
save_fpstate(dcontext, &frame);
#endif
/* The kernel calls do_sigaltstack on sys_rt_sigreturn primarily to ensure
* the frame is ok, but the side effect is we can mess up our own altstack
* settings if we're not careful. Having invalid ss_size looks good for
* kernel 2.6.23.9 at least so we leave frame.uc.uc_stack as all zeros.
*/
/* make sure sigreturn's mask setting doesn't change anything */
sigprocmask_syscall(SIG_SETMASK, NULL, (kernel_sigset_t *)&frame.uc.uc_sigmask,
sizeof(frame.uc.uc_sigmask));
LOG(THREAD_GET, LOG_ASYNCH, 2, "thread_set_self_context: pc=" PFX "\n", sc->SC_XIP);
LOG(THREAD_GET, LOG_ASYNCH, 3, "full sigcontext\n");
DOLOG(LOG_ASYNCH, 3, {
dcontext_t *dc = get_thread_private_dcontext();
dump_sigcontext(dc, get_sigcontext_from_rt_frame(&frame));
});
/* set up xsp to point at &frame */
/* For x86 only, we need to skip pretcode while setting xsp. */
xsp_for_sigreturn = ((app_pc)&frame)IF_X86(+sizeof(char *));
#ifdef DR_HOST_NOT_TARGET
ASSERT_NOT_REACHED();
#elif defined(X86)
asm("mov %0, %%" ASM_XSP : : "m"(xsp_for_sigreturn));
# ifdef MACOS
ASSERT_NOT_IMPLEMENTED(false && "need to pass 2 params to SYS_sigreturn");
asm("jmp _dynamorio_sigreturn");
# else
/* i#2632: recent clang for 32-bit annoyingly won't do the right thing for
* "jmp dynamorio_sigreturn" and leaves relocs so we ensure it's PIC:
*/
void (*asm_jmp_tgt)() = dynamorio_sigreturn;
asm("mov %0, %%" ASM_XCX : : "m"(asm_jmp_tgt));
asm("jmp *%" ASM_XCX);
# endif /* MACOS/LINUX */
#elif defined(AARCH64)
asm("mov " ASM_XSP ", %0" : : "r"(xsp_for_sigreturn));
asm("b dynamorio_sigreturn");
#elif defined(ARM)
asm("ldr " ASM_XSP ", %0" : : "m"(xsp_for_sigreturn));
asm("b dynamorio_sigreturn");
#endif /* X86/AARCH64/ARM */
ASSERT_NOT_REACHED();
}
static void
thread_set_segment_registers(sigcontext_t *sc)
{
#ifdef X86
/* Fill in the segment registers */
__asm__ __volatile__("mov %%cs, %%ax; mov %%ax, %0"
: "=m"(sc->SC_FIELD(cs))
:
: "eax");
# ifndef X64
__asm__ __volatile__("mov %%ss, %%ax; mov %%ax, %0"
: "=m"(sc->SC_FIELD(ss))
:
: "eax");
__asm__ __volatile__("mov %%ds, %%ax; mov %%ax, %0"
: "=m"(sc->SC_FIELD(ds))
:
: "eax");
__asm__ __volatile__("mov %%es, %%ax; mov %%ax, %0"
: "=m"(sc->SC_FIELD(es))
:
: "eax");
# endif
__asm__ __volatile__("mov %%fs, %%ax; mov %%ax, %0"
: "=m"(sc->SC_FIELD(fs))
:
: "eax");
__asm__ __volatile__("mov %%gs, %%ax; mov %%ax, %0"
: "=m"(sc->SC_FIELD(gs))
:
: "eax");
#endif
}
/* Takes a priv_mcontext_t */
void
thread_set_self_mcontext(priv_mcontext_t *mc)
{
kernel_ucontext_t ucxt;
sig_full_cxt_t sc_full;
sig_full_initialize(&sc_full, &ucxt);
#if defined(LINUX) && defined(X86)
sc_full.sc->fpstate = NULL; /* for mcontext_to_sigcontext */
#endif
mcontext_to_sigcontext(&sc_full, mc, DR_MC_ALL);
thread_set_segment_registers(sc_full.sc);
/* sigreturn takes the mode from cpsr */
IF_ARM(
set_pc_mode_in_cpsr(sc_full.sc, dr_get_isa_mode(get_thread_private_dcontext())));
/* thread_set_self_context will fill in the real fp/simd state for x86 */
thread_set_self_context((void *)sc_full.sc);
ASSERT_NOT_REACHED();
}
#ifdef LINUX
static bool
sig_has_restorer(thread_sig_info_t *info, int sig)
{
# ifdef VMX86_SERVER
/* vmkernel ignores SA_RESTORER (PR 405694) */
return false;
# endif
if (info->app_sigaction[sig] == NULL)
return false;
if (TEST(SA_RESTORER, info->app_sigaction[sig]->flags))
return true;
if (info->app_sigaction[sig]->restorer == NULL)
return false;
/* we cache the result due to the safe_read cost */
if (info->restorer_valid[sig] == -1) {
/* With older kernels, don't seem to need flag: if sa_restorer !=
* NULL kernel will use it. But with newer kernels that's not
* true, and sometimes libc does pass non-NULL.
*/
# ifdef X86
/* Signal restorer code for Ubuntu 7.04:
* 0xffffe420 <__kernel_sigreturn+0>: pop %eax
* 0xffffe421 <__kernel_sigreturn+1>: mov $0x77,%eax
* 0xffffe426 <__kernel_sigreturn+6>: int $0x80
*
* 0xffffe440 <__kernel_rt_sigreturn+0>: mov $0xad,%eax
* 0xffffe445 <__kernel_rt_sigreturn+5>: int $0x80
*/
static const byte SIGRET_NONRT[8] = { 0x58, 0xb8, 0x77, 0x00,
0x00, 0x00, 0xcd, 0x80 };
static const byte SIGRET_RT[8] = { 0xb8, 0xad, 0x00, 0x00, 0x00, 0xcd, 0x80 };
# elif defined(ARM)
static const byte SIGRET_NONRT[8] = { 0x77, 0x70, 0xa0, 0xe3,
0x00, 0x00, 0x00, 0xef };
static const byte SIGRET_RT[8] = {
0xad, 0x70, 0xa0, 0xe3, 0x00, 0x00, 0x00, 0xef
};
# elif defined(AARCH64)
static const byte SIGRET_NONRT[8] = { 0 }; /* unused */
static const byte SIGRET_RT[8] =
/* FIXME i#1569: untested */
/* mov w8, #139 ; svc #0 */
{ 0x68, 0x11, 0x80, 0x52, 0x01, 0x00, 0x00, 0xd4 };
# endif
byte buf[MAX(sizeof(SIGRET_NONRT), sizeof(SIGRET_RT))] = { 0 };
if (d_r_safe_read(info->app_sigaction[sig]->restorer, sizeof(buf), buf) &&
((IS_RT_FOR_APP(info, sig) &&
memcmp(buf, SIGRET_RT, sizeof(SIGRET_RT)) == 0) ||
(!IS_RT_FOR_APP(info, sig) &&
memcmp(buf, SIGRET_NONRT, sizeof(SIGRET_NONRT)) == 0))) {
LOG(THREAD_GET, LOG_ASYNCH, 2,
"sig_has_restorer %d: " PFX " looks like restorer, using w/o flag\n", sig,
info->app_sigaction[sig]->restorer);
info->restorer_valid[sig] = 1;
} else
info->restorer_valid[sig] = 0;
}
return (info->restorer_valid[sig] == 1);
}
#endif
/* Returns the size of the frame for delivering to the app.
* For x64 this does NOT include kernel_fpstate_t.
*/
static uint
get_app_frame_size(thread_sig_info_t *info, int sig)
{
if (IS_RT_FOR_APP(info, sig))
return sizeof(sigframe_rt_t);
#ifdef LINUX
else
return sizeof(sigframe_plain_t);
#endif
}
static kernel_ucontext_t *
get_ucontext_from_rt_frame(sigframe_rt_t *frame)
{
#if defined(MACOS) && !defined(X64)
/* Padding makes it unsafe to access uc on frame from kernel */
return frame->puc;
#else
return &frame->uc;
#endif
}
sigcontext_t *
get_sigcontext_from_rt_frame(sigframe_rt_t *frame)
{
return SIGCXT_FROM_UCXT(get_ucontext_from_rt_frame(frame));
}
static sigcontext_t *
get_sigcontext_from_app_frame(thread_sig_info_t *info, int sig, void *frame)
{
sigcontext_t *sc = NULL; /* initialize to satisfy Mac clang */
bool rtframe = IS_RT_FOR_APP(info, sig);
if (rtframe)
sc = get_sigcontext_from_rt_frame((sigframe_rt_t *)frame);
#ifdef LINUX
else {
# ifdef X86
sc = (sigcontext_t *)&(((sigframe_plain_t *)frame)->sc);
# elif defined(ARM)
sc = SIGCXT_FROM_UCXT(&(((sigframe_plain_t *)frame)->uc));
# else
ASSERT_NOT_REACHED();
# endif
}
#endif
return sc;
}
static sigcontext_t *
get_sigcontext_from_pending(thread_sig_info_t *info, int sig)
{
ASSERT(info->sigpending[sig] != NULL);
return get_sigcontext_from_rt_frame(&info->sigpending[sig]->rt_frame);
}
/* Returns the address on the appropriate signal stack where we should copy
* the frame.
* If frame is NULL, assumes signal happened while in DR and has been delayed,
* and thus we need to provide fpstate regardless of whether the original
* had it. If frame is non-NULL, matches frame's amount of fpstate.
* dcontext can be NULL if frame is non-NULL.
*/
static byte *
get_sigstack_frame_ptr(dcontext_t *dcontext, thread_sig_info_t *info, int sig,
sigframe_rt_t *frame)
{
ASSERT(dcontext != NULL || frame != NULL);
sigcontext_t *sc = (frame == NULL) ? get_sigcontext_from_pending(info, sig)
: get_sigcontext_from_rt_frame(frame);
byte *sp;
if (frame != NULL) {
/* Signal happened natively or while in the cache: grab interrupted xsp. */
sp = (byte *)sc->SC_XSP;
LOG(THREAD, LOG_ASYNCH, 3, "get_sigstack_frame_ptr: using frame's xsp " PFX "\n",
sp);
} else {
/* signal happened while in DR, use stored xsp */
sp = (byte *)get_mcontext(dcontext)->xsp;
LOG(THREAD, LOG_ASYNCH, 3, "get_sigstack_frame_ptr: using app xsp " PFX "\n", sp);
}
if (USE_APP_SIGSTACK(info, sig)) {
/* app has own signal stack which is enabled for this handler */
LOG(THREAD, LOG_ASYNCH, 3, "get_sigstack_frame_ptr: app has own stack " PFX "\n",
info->app_sigstack.ss_sp);
LOG(THREAD, LOG_ASYNCH, 3, "\tcur sp=" PFX " vs app stack " PFX "-" PFX "\n", sp,
info->app_sigstack.ss_sp,
info->app_sigstack.ss_sp + info->app_sigstack.ss_size);
if (sp > (byte *)info->app_sigstack.ss_sp &&
sp - (byte *)info->app_sigstack.ss_sp < info->app_sigstack.ss_size) {
/* we're currently in the alt stack, so use current xsp */
LOG(THREAD, LOG_ASYNCH, 3,
"\tinside alt stack, so using current xsp " PFX "\n", sp);
} else {
/* need to go to top, stack grows down */
sp = info->app_sigstack.ss_sp + info->app_sigstack.ss_size;
LOG(THREAD, LOG_ASYNCH, 3,
"\tnot inside alt stack, so using base xsp " PFX "\n", sp);
}
}
if (frame != NULL) {
/* Handle DR's frame already being on the app stack. For native delivery we
* could try to re-use this frame, but that doesn't work with plain vs rt.
* Instead we move below and live with the downsides of a potential stack
* overflow.
*/
size_t frame_sz_max = sizeof(sigframe_rt_t) + REDZONE_SIZE +
IF_LINUX(IF_X86((sc->fpstate == NULL ? 0
: signal_frame_extra_size(
true)) +)) 64 /* align + slop */;
if (sp > (byte *)frame && sp < (byte *)frame + frame_sz_max) {
sp = (byte *)frame;
/* We're probably *on* the stack right where we want to put the frame! */
byte *cur_sp;
GET_STACK_PTR(cur_sp);
/* We have to copy the frame below our own stack usage high watermark
* in the rest of the code we'll execute from execute_native_handler().
* Kind of a mess. For now we estimate two pages which should be plenty
* and still not be egregious usage for most app stacks. For the altstack
* we check the size below.
*/
#define EXECUTE_NATIVE_STACK_USAGE (4096 * 2)
if (sp > cur_sp && sp - frame_sz_max - EXECUTE_NATIVE_STACK_USAGE < cur_sp) {
sp = cur_sp - frame_sz_max - EXECUTE_NATIVE_STACK_USAGE;
}
if (USE_APP_SIGSTACK(info, sig)) {
#define APP_ALTSTACK_USAGE (SIGSTKSZ / 2)
if (sp - APP_ALTSTACK_USAGE < (byte *)info->app_sigstack.ss_sp) {
/* There's not enough stack space. The only solution would be to
* re-use DR's frame and limit our own stack usage here, which
* gets complex. We bail.
*/
report_unhandleable_signal_and_exit(
sig, "sigaltstack too small in native thread");
ASSERT_NOT_REACHED();
}
}
LOG(THREAD, LOG_ASYNCH, 3,
"get_sigstack_frame_ptr: moving beyond same-stack frame to %p\n", sp);
}
}
/* now get frame pointer: need to go down to first field of frame */
sp -= get_app_frame_size(info, sig);
#if defined(LINUX) && defined(X86)
if (frame == NULL) {
/* XXX i#641: we always include space for full xstate,
* even if we don't use it all, which does not match what the
* kernel does, but we're not tracking app actions to know whether
* we can skip lazy fpstate on the delay
*/
sp -= signal_frame_extra_size(true);
} else {
if (sc->fpstate != NULL) {
/* The kernel doesn't seem to lazily include avx, so we don't either,
* which simplifies all our frame copying: if YMM_ENABLED() and the
* fpstate pointer is non-NULL, then we assume there's space for
* full xstate
*/
sp -= signal_frame_extra_size(true);
DOCHECK(1, {
ASSERT(YMM_ENABLED() || !ZMM_ENABLED());
if (YMM_ENABLED()) {
ASSERT_CURIOSITY(sc->fpstate->sw_reserved.magic1 == FP_XSTATE_MAGIC1);
ASSERT(sc->fpstate->sw_reserved.extended_size <=
signal_frame_extra_size(true));
}
});
}
}
#endif /* LINUX && X86 */
/* PR 369907: don't forget the redzone */
sp -= REDZONE_SIZE;
/* Align to 16-bytes. The kernel does this for both 32 and 64-bit code
* these days, so we do as well.
*/
sp = (byte *)ALIGN_BACKWARD(sp, 16);
IF_X86(sp -= sizeof(reg_t)); /* Model retaddr. */
LOG(THREAD, LOG_ASYNCH, 3, "\tplacing frame at " PFX "\n", sp);
return sp;
}
#if defined(LINUX) && !defined(X64)
static void
convert_rt_mask_to_nonrt(sigframe_plain_t *f_plain, kernel_sigset_t *sigmask)
{
# ifdef X86
f_plain->sc.oldmask = sigmask->sig[0];
memcpy(&f_plain->extramask, &sigmask->sig[1], (_NSIG_WORDS - 1) * sizeof(uint));
# elif defined(ARM)
f_plain->uc.uc_mcontext.oldmask = sigmask->sig[0];
memcpy(&f_plain->uc.sigset_ex, &sigmask->sig[1], (_NSIG_WORDS - 1) * sizeof(uint));
# else
# error NYI
# endif
}
static void
convert_frame_to_nonrt(dcontext_t *dcontext, int sig, sigframe_rt_t *f_old,
sigframe_plain_t *f_new)
{
# ifdef X86
sigcontext_t *sc_old = get_sigcontext_from_rt_frame(f_old);
f_new->pretcode = f_old->pretcode;
f_new->sig = f_old->sig;
memcpy(&f_new->sc, get_sigcontext_from_rt_frame(f_old), sizeof(sigcontext_t));
if (sc_old->fpstate != NULL) {
/* up to caller to include enough space for fpstate at end */
byte *new_fpstate =
(byte *)ALIGN_FORWARD(((byte *)f_new) + sizeof(*f_new), XSTATE_ALIGNMENT);
memcpy(new_fpstate, sc_old->fpstate, signal_frame_extra_size(false));
f_new->sc.fpstate = (kernel_fpstate_t *)new_fpstate;
}
convert_rt_mask_to_nonrt(f_new, &f_old->uc.uc_sigmask);
memcpy(&f_new->retcode, &f_old->retcode, RETCODE_SIZE);
/* now fill in our extra field */
f_new->sig_noclobber = f_new->sig;
# elif defined(ARM)
memcpy(&f_new->uc, &f_old->uc, sizeof(f_new->uc));
memcpy(f_new->retcode, f_old->retcode, sizeof(f_new->retcode));
/* now fill in our extra field */
f_new->sig_noclobber = f_old->info.si_signo;
# endif /* X86 */
LOG(THREAD, LOG_ASYNCH, 3, "\tconverted sig=%d rt frame to non-rt frame\n",
f_new->sig_noclobber);
}
#endif
/* Exported for call from master_signal_handler asm routine.
* For the rt signal frame f_old that was copied to f_new, updates
* the intra-frame absolute pointers to point to the new addresses
* in f_new.
* Only updates the pretcode to the stored app restorer if for_app.
* dcontext can be NULL (which is why we take in info).
*/
static void
fixup_rtframe_pointers(dcontext_t *dcontext, thread_sig_info_t *info, int sig,
sigframe_rt_t *f_old, sigframe_rt_t *f_new, bool for_app,
size_t f_new_alloc_size)
{
ASSERT(info != NULL);
#if defined(X86) && defined(LINUX)
bool has_restorer = sig_has_restorer(info, sig);
# ifdef DEBUG
uint level = 3;
# if !defined(HAVE_MEMINFO)
/* avoid logging every single TRY probe fault */
if (!dynamo_initialized)
level = 5;
# endif
# endif
if (has_restorer && for_app)
f_new->pretcode = (char *)info->app_sigaction[sig]->restorer;
else {
# ifdef VMX86_SERVER
/* PR 404712: skip kernel's restorer code */
if (for_app)
f_new->pretcode = (char *)dynamorio_sigreturn;
# else
# ifdef X64
ASSERT(!for_app || doing_detach); /* detach uses a frame to go native */
# else
/* only point at retcode if old one was -- with newer OS, points at
* vsyscall page and there is no restorer, yet stack restorer code left
* there for gdb compatibility
*/
if (f_old->pretcode == f_old->retcode)
f_new->pretcode = f_new->retcode;
/* else, pointing at vsyscall, or we set it to dynamorio_sigreturn in
* master_signal_handler
*/
LOG(THREAD, LOG_ASYNCH, level, "\tleaving pretcode with old value\n");
# endif
# endif
}
# ifndef X64
f_new->pinfo = &(f_new->info);
f_new->puc = &(f_new->uc);
# endif
if (f_old->uc.uc_mcontext.fpstate != NULL) {
uint frame_size = get_app_frame_size(info, sig);
byte *frame_end = ((byte *)f_new) + frame_size;
byte *tgt = (byte *)ALIGN_FORWARD(frame_end, XSTATE_ALIGNMENT);
size_t extra_size = signal_frame_extra_size(false);
ASSERT(extra_size == f_new->uc.uc_mcontext.fpstate->sw_reserved.extended_size);
/* XXX: It may be better to move this into memcpy_rt_frame (or another
* routine, since copy_frame_to_pending() wants them separate), since
* fixup_rtframe_pointers() was originally meant to just update a few
* pointers and not do a big memcpy. Compare to MACOS which calls it in
* copy_frame_to_pending(): if Linux did that we'd have memory clobbering.
*/
ASSERT(tgt + extra_size <= (byte *)f_new + f_new_alloc_size);
memcpy(tgt, f_new->uc.uc_mcontext.fpstate, extra_size);
f_new->uc.uc_mcontext.fpstate = (kernel_fpstate_t *)tgt;
LOG(THREAD, LOG_ASYNCH, level + 1, "\tfpstate old=" PFX " new=" PFX "\n",
f_old->uc.uc_mcontext.fpstate, f_new->uc.uc_mcontext.fpstate);
/* Be sure we're keeping both magic words. Failure to do so causes the
* kernel to only restore x87 and SSE state and zero out all AVX+ state
* (i#3812).
*/
ASSERT(f_new->uc.uc_mcontext.fpstate->sw_reserved.magic1 ==
f_old->uc.uc_mcontext.fpstate->sw_reserved.magic1);
ASSERT((f_new->uc.uc_mcontext.fpstate->sw_reserved.magic1 == 0 &&
f_new->uc.uc_mcontext.fpstate->sw_reserved.extended_size ==
sizeof(kernel_fpstate_t)) ||
(f_new->uc.uc_mcontext.fpstate->sw_reserved.magic1 == FP_XSTATE_MAGIC1 &&
*(int *)((byte *)f_new->uc.uc_mcontext.fpstate +
f_new->uc.uc_mcontext.fpstate->sw_reserved.extended_size -
FP_XSTATE_MAGIC2_SIZE) == FP_XSTATE_MAGIC2));
} else {
/* if fpstate is not set up, we're delivering signal immediately,
* and we shouldn't need an fpstate since DR code won't modify it;
* only if we delayed will we need it, and when delaying we make
* room and set up the pointer in copy_frame_to_pending.
* xref i#641.
*/
LOG(THREAD, LOG_ASYNCH, level + 1, "\tno fpstate needed\n");
}
LOG(THREAD, LOG_ASYNCH, level, "\tretaddr = " PFX "\n", f_new->pretcode);
# ifdef RETURN_AFTER_CALL
info->signal_restorer_retaddr = (app_pc)f_new->pretcode;
# endif
/* 32-bit kernel copies to aligned buf first */
IF_X64(ASSERT(ALIGNED(f_new->uc.uc_mcontext.fpstate, 16)));
#elif defined(MACOS)
# ifdef X64
/* Nothing to do. */
# else
f_new->pinfo = &(f_new->info);
f_new->puc = &(f_new->uc);
f_new->puc->uc_mcontext =
(IF_X64_ELSE(_STRUCT_MCONTEXT64, _STRUCT_MCONTEXT32) *)&f_new->mc;
LOG(THREAD, LOG_ASYNCH, 3, "\tf_new=" PFX ", &handler=" PFX "\n", f_new,
&f_new->handler);
ASSERT(!for_app || ALIGNED(&f_new->handler, 16));
# endif
#endif /* X86 && LINUX */
}
/* Only operates on rt frames, so call before converting to plain.
* Must be called *after* translating the sigcontext.
*/
static void
fixup_siginfo(dcontext_t *dcontext, int sig, sigframe_rt_t *frame)
{
/* For some signals, si_addr is a PC which we must translate. */
if (sig != SIGILL && sig != SIGTRAP && sig != SIGFPE)
return; /* nothing to do */
sigcontext_t *sc = get_sigcontext_from_rt_frame(frame);
kernel_siginfo_t *siginfo = SIGINFO_FROM_RT_FRAME(frame);
LOG(THREAD, LOG_ASYNCH, 3, "%s: updating si_addr from " PFX " to " PFX "\n",
__FUNCTION__, siginfo->si_addr, sc->SC_XIP);
siginfo->si_addr = (void *)sc->SC_XIP;
#ifdef LINUX
siginfo->si_addr_lsb = sc->SC_XIP & 0x1;
#endif
}
static void
memcpy_rt_frame(sigframe_rt_t *frame, byte *dst, bool from_pending)
{
#if defined(MACOS) && !defined(X64)
if (!from_pending) {
/* The kernel puts padding in the middle. We collapse that padding here
* and re-align when we copy to the app stack.
* We should not reference fields from mc onward in what the kernel put
* on the stack, as our sigframe_rt_t layout does not match the kernel's
* variable mid-struct padding.
*/
sigcontext_t *sc = SIGCXT_FROM_UCXT(frame->puc);
memcpy(dst, frame, offsetof(sigframe_rt_t, puc) + sizeof(frame->puc));
memcpy(&((sigframe_rt_t *)dst)->mc, sc,
sizeof(sigframe_rt_t) - offsetof(sigframe_rt_t, mc));
return;
}
#endif
/* Ensure no overlap. */
ASSERT(dst >= (byte *)(frame + 1) + signal_frame_extra_size(true) ||
dst + sizeof(sigframe_rt_t) + signal_frame_extra_size(true) <= (byte *)frame);
memcpy(dst, frame, sizeof(sigframe_rt_t));
}
/* Copies frame to sp.
* PR 304708: we now leave in rt form right up until we copy to the
* app stack, so that we can deliver to a client at a safe spot
* in rt form, so this routine now converts to a plain frame if necessary.
* If no restorer, touches up pretcode
* (and if rt_frame, touches up pinfo and puc)
* Also touches up fpstate pointer
* dcontext can be NULL (which is why we take in info) in which case no check
* versus executable memory is performed (we assume this is a native frame).
*/
static void
copy_frame_to_stack(dcontext_t *dcontext, thread_sig_info_t *info, int sig,
sigframe_rt_t *frame, byte *sp, bool from_pending)
{
bool rtframe = IS_RT_FOR_APP(info, sig);
uint frame_size = get_app_frame_size(info, sig);
#if defined(LINUX) && defined(X86_32)
bool has_restorer = sig_has_restorer(info, sig);
#endif
byte *flush_pc;
bool stack_unwritable = false;
uint size = frame_size;
#if defined(LINUX) && defined(X86)
sigcontext_t *sc = get_sigcontext_from_rt_frame(frame);
size += (sc->fpstate == NULL ? 0 : signal_frame_extra_size(true));
#endif /* LINUX && X86 */
LOG(THREAD, LOG_ASYNCH, 3, "copy_frame_to_stack: rt=%d, src=" PFX ", sp=" PFX "\n",
rtframe, frame, sp);
fixup_siginfo(dcontext, sig, frame);
/* We avoid querying memory as it incurs global contended locks. */
flush_pc =
dcontext == NULL ? false : is_executable_area_writable_overlap(sp, sp + size);
if (flush_pc != NULL) {
LOG(THREAD, LOG_ASYNCH, 2,
"\tcopy_frame_to_stack: part of stack is unwritable-by-us @" PFX "\n",
flush_pc);
flush_fragments_and_remove_region(dcontext, flush_pc, sp + size - flush_pc,
false /* don't own initexit_lock */,
false /* keep futures */);
}
ASSERT(!rtframe || frame_size == sizeof(*frame));
if (dcontext == NULL) {
/* We have no safe-read mechanism so we do the best we can: blindly copy. */
if (rtframe)
memcpy_rt_frame(frame, sp, from_pending);
IF_NOT_X64(IF_LINUX(
else convert_frame_to_nonrt(dcontext, sig, frame, (sigframe_plain_t *)sp);));
} else {
TRY_EXCEPT(dcontext, /* try */
{
if (rtframe)
memcpy_rt_frame(frame, sp, from_pending);
IF_NOT_X64(
IF_LINUX(else convert_frame_to_nonrt(
dcontext, sig, frame, (sigframe_plain_t *)sp);));
},
/* except */ { stack_unwritable = true; });
}
if (stack_unwritable) {
/* Override the no-nested check in record_pending_signal(): it's ok b/c
* receive_pending_signal() calls to here at a consistent point,
* and we won't return there.
*/
info->nested_pending_ok = true;
/* Just throw away this signal and deliver SIGSEGV instead with the
* same sigcontext, like the kernel does.
*/
free_pending_signal(info, sig);
os_forge_exception(0, UNREADABLE_MEMORY_EXECUTION_EXCEPTION);
ASSERT_NOT_REACHED();
}
kernel_sigset_t *mask_to_restore = NULL;
if (info->pre_syscall_app_sigprocmask_valid) {
mask_to_restore = &info->pre_syscall_app_sigprocmask;
info->pre_syscall_app_sigprocmask_valid = false;
} else {
mask_to_restore = &info->app_sigblocked;
}
/* if !has_restorer we do NOT add the restorer code to the exec list here,
* to avoid removal problems (if handler never returns) and consistency problems
* (would have to mark as selfmod right now if on stack).
* for PROGRAM_SHEPHERDING we recognize as a pattern, and for consistency we
* allow entire region once try to execute -- not a performance worry since should
* very rarely be on the stack: should either be libc restorer code or with recent
* OS in rx vsyscall page.
*/
/* fix up pretcode, pinfo, puc, fpstate */
if (rtframe) {
sigframe_rt_t *f_new = (sigframe_rt_t *)sp;
fixup_rtframe_pointers(dcontext, info, sig, frame, f_new, true /*for app*/, size);
#ifdef HAVE_SIGALTSTACK
/* Make sure the frame's sigstack reflects the app stack, both for transparency
* of the app examining it and for correctness if we detach mid-handler.
*/
LOG(THREAD, LOG_ASYNCH, 3, "updated uc_stack @" PFX " to " PFX "\n",
&f_new->uc.uc_stack, info->app_sigstack.ss_sp);
f_new->uc.uc_stack = info->app_sigstack;
#endif
/* Store the prior mask, for restoring in sigreturn. */
memcpy(&f_new->uc.uc_sigmask, mask_to_restore, sizeof(info->app_sigblocked));
} else {
#ifdef X64
ASSERT_NOT_REACHED();
#endif
#if defined(LINUX) && !defined(X64)
sigframe_plain_t *f_new = (sigframe_plain_t *)sp;
# ifdef X86
# ifndef VMX86_SERVER
sigframe_plain_t *f_old = (sigframe_plain_t *)frame;
# endif
if (has_restorer)
f_new->pretcode = (char *)info->app_sigaction[sig]->restorer;
else {
# ifdef VMX86_SERVER
/* PR 404712: skip kernel's restorer code */
f_new->pretcode = (char *)dynamorio_nonrt_sigreturn;
# else
/* see comments in rt case above */
if (f_old->pretcode == f_old->retcode)
f_new->pretcode = f_new->retcode;
else {
/* whether we set to dynamorio_sigreturn in master_signal_handler
* or it's still vsyscall page, we have to convert to non-rt
*/
f_new->pretcode = (char *)dynamorio_nonrt_sigreturn;
} /* else, pointing at vsyscall most likely */
LOG(THREAD, LOG_ASYNCH, 3, "\tleaving pretcode with old value\n");
# endif
}
/* convert_frame_to_nonrt*() should have updated fpstate pointer.
* The inlined fpstate is no longer used on new kernels, and we do that
* as well on older kernels.
*/
ASSERT(f_new->sc.fpstate != &f_new->fpstate);
/* 32-bit kernel copies to aligned buf so no assert on fpstate alignment */
LOG(THREAD, LOG_ASYNCH, 3, "\tretaddr = " PFX "\n", f_new->pretcode);
/* There is no stored alt stack in a plain frame to update. */
# ifdef RETURN_AFTER_CALL
info->signal_restorer_retaddr = (app_pc)f_new->pretcode;
# endif
# endif /* X86 */
/* Store the prior mask, for restoring in sigreturn. */
convert_rt_mask_to_nonrt(f_new, mask_to_restore);
#endif /* LINUX && !X64 */
}
#if defined(MACOS) && !defined(X64)
/* Update handler field, which is passed to the libc trampoline, to app */
ASSERT(info->app_sigaction[sig] != NULL);
((sigframe_rt_t *)sp)->handler = (app_pc)info->app_sigaction[sig]->handler;
#endif
}
/* Copies frame to pending slot.
* PR 304708: we now leave in rt form right up until we copy to the
* app stack, so that we can deliver to a client at a safe spot
* in rt form.
*/
static void
copy_frame_to_pending(dcontext_t *dcontext, int sig,
sigframe_rt_t *frame _IF_CLIENT(byte *access_address))
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
sigframe_rt_t *dst = &(info->sigpending[sig]->rt_frame);
memcpy_rt_frame(frame, (byte *)dst, false /*!already pending*/);
#if defined(LINUX) && defined(X86)
/* For lazy fpstate, it's possible there was no fpstate when the kernel
* sent us the frame, but in between then and now the app executed some
* fp or xmm/ymm instrs. Today we always add fpstate just in case.
* XXX i#641 optimization: track whether any fp/xmm/ymm
* instrs happened and avoid this.
*/
/* we'll fill in updated fpstate at delivery time, but we go ahead and
* copy now in case our own retrieval somehow misses some fields
*/
if (frame->uc.uc_mcontext.fpstate != NULL) {
size_t extra_size = signal_frame_extra_size(false);
ASSERT(extra_size == frame->uc.uc_mcontext.fpstate->sw_reserved.extended_size);
memcpy(&info->sigpending[sig]->xstate, frame->uc.uc_mcontext.fpstate, extra_size);
}
/* we must set the pointer now so that later save_fpstate, etc. work */
dst->uc.uc_mcontext.fpstate = (kernel_fpstate_t *)&info->sigpending[sig]->xstate;
#endif /* LINUX && X86 */
#ifdef CLIENT_INTERFACE
info->sigpending[sig]->access_address = access_address;
#endif
info->sigpending[sig]->use_sigcontext = false;
#ifdef MACOS
/* We rely on puc to find sc to we have to fix it up */
fixup_rtframe_pointers(dcontext, info, sig, frame, dst, false /*!for app*/,
sizeof(*dst));
#endif
LOG(THREAD, LOG_ASYNCH, 3, "copy_frame_to_pending from " PFX "\n", frame);
DOLOG(3, LOG_ASYNCH, {
LOG(THREAD, LOG_ASYNCH, 3, "sigcontext:\n");
dump_sigcontext(dcontext, get_sigcontext_from_rt_frame(dst));
});
}
/**** real work ***********************************************/
/* transfer control from signal handler to fcache return routine */
static void
transfer_from_sig_handler_to_fcache_return(dcontext_t *dcontext, kernel_ucontext_t *uc,
sigcontext_t *sc_interrupted, int sig,
app_pc next_pc, linkstub_t *last_exit,
bool is_kernel_xfer)
{
sigcontext_t *sc = SIGCXT_FROM_UCXT(uc);
#ifdef CLIENT_INTERFACE
if (is_kernel_xfer) {
sig_full_cxt_t sc_interrupted_full = { sc_interrupted, NULL /*not provided*/ };
sig_full_cxt_t sc_full;
sig_full_initialize(&sc_full, uc);
sc->SC_XIP = (ptr_uint_t)next_pc;
/* i#4041: Provide the actually-interrupted mid-rseq PC to this event. */
ptr_uint_t official_xl8 = sc_interrupted->SC_XIP;
sc_interrupted->SC_XIP =
(ptr_uint_t)translate_last_direct_translation(dcontext, (app_pc)official_xl8);
if (instrument_kernel_xfer(dcontext, DR_XFER_SIGNAL_DELIVERY, sc_interrupted_full,
NULL, NULL, next_pc, sc->SC_XSP, sc_full, NULL, sig))
next_pc = (app_pc)sc->SC_XIP;
sc_interrupted->SC_XIP = official_xl8;
}
#endif
dcontext->next_tag = canonicalize_pc_target(dcontext, next_pc);
IF_ARM(dr_set_isa_mode(dcontext, get_pc_mode_from_cpsr(sc), NULL));
/* Set our sigreturn context to point to fcache_return!
* Then we'll go back through kernel, appear in fcache_return,
* and go through d_r_dispatch & interp, without messing up dynamo stack.
* Note that even if this is a write in the shared cache, we
* still go to the private fcache_return for simplicity.
*/
sc->SC_XIP = (ptr_uint_t)fcache_return_routine(dcontext);
#ifdef AARCHXX
/* We do not have to set dr_reg_stolen in dcontext's mcontext here
* because dcontext's mcontext is stale and we used the mcontext
* created from recreate_app_state_internal with the original sigcontext.
*/
/* We restore dr_reg_stolen's app value in recreate_app_state_internal,
* so now we need set dr_reg_stolen to hold DR's TLS before sigreturn
* from DR's handler.
*/
ASSERT(get_sigcxt_stolen_reg(sc) != (reg_t)*get_dr_tls_base_addr());
/* Preserve the translated value. */
dcontext->local_state->spill_space.reg_stolen = get_sigcxt_stolen_reg(sc);
/* Now put DR's base in the sigcontext. */
set_sigcxt_stolen_reg(sc, (reg_t)*get_dr_tls_base_addr());
# ifndef AARCH64
/* We're going to our fcache_return gencode which uses DEFAULT_ISA_MODE */
set_pc_mode_in_cpsr(sc, DEFAULT_ISA_MODE);
# endif
#endif
#if defined(X64) || defined(ARM)
/* x64 always uses shared gencode */
dcontext->local_state->spill_space.IF_X86_ELSE(xax, r0) =
sc->IF_X86_ELSE(SC_XAX, SC_R0);
# ifdef AARCH64
/* X1 needs to be spilled because of br x1 in exit stubs. */
dcontext->local_state->spill_space.r1 = sc->SC_R1;
# endif
#else
get_mcontext(dcontext)->IF_X86_ELSE(xax, r0) = sc->IF_X86_ELSE(SC_XAX, SC_R0);
#endif
LOG(THREAD, LOG_ASYNCH, 2, "\tsaved xax " PFX "\n", sc->IF_X86_ELSE(SC_XAX, SC_R0));
sc->IF_X86_ELSE(SC_XAX, SC_R0) = (ptr_uint_t)last_exit;
LOG(THREAD, LOG_ASYNCH, 2, "\tset next_tag to " PFX ", resuming in fcache_return\n",
next_pc);
LOG(THREAD, LOG_ASYNCH, 3, "transfer_from_sig_handler_to_fcache_return\n");
DOLOG(3, LOG_ASYNCH, {
LOG(THREAD, LOG_ASYNCH, 3, "sigcontext @" PFX ":\n", sc);
dump_sigcontext(dcontext, sc);
});
}
static void
restore_orig_sigcontext(sigframe_rt_t *frame, sigcontext_t *sc_orig)
{
ASSERT(frame != NULL && sc_orig != NULL);
sigcontext_t *sc = get_sigcontext_from_rt_frame(frame);
#if defined(X86) && defined(LINUX)
kernel_fpstate_t *fpstate = sc->fpstate;
if (fpstate != NULL) {
ASSERT(sc_orig->fpstate != NULL);
*fpstate = *sc_orig->fpstate;
sc_orig->fpstate = fpstate;
}
#endif
*sc = *sc_orig;
}
#ifdef CLIENT_INTERFACE
static dr_signal_action_t
send_signal_to_client(dcontext_t *dcontext, int sig, sigframe_rt_t *frame,
sigcontext_t *raw_sc, byte *access_address, bool blocked,
fragment_t *fragment)
{
kernel_ucontext_t *uc = get_ucontext_from_rt_frame(frame);
dr_siginfo_t si;
dr_signal_action_t action;
sig_full_cxt_t raw_sc_full;
sig_full_initialize(&raw_sc_full, uc);
raw_sc_full.sc = raw_sc;
if (!dr_signal_hook_exists())
return DR_SIGNAL_DELIVER;
LOG(THREAD, LOG_ASYNCH, 2, "sending signal to client\n");
si.sig = sig;
si.drcontext = (void *)dcontext;
/* It's safe to allocate since we do not send signals that interrupt DR.
* With priv_mcontext_t x2 that's a little big for stack alloc.
*/
si.mcontext = heap_alloc(dcontext, sizeof(*si.mcontext) HEAPACCT(ACCT_OTHER));
si.raw_mcontext = heap_alloc(dcontext, sizeof(*si.raw_mcontext) HEAPACCT(ACCT_OTHER));
dr_mcontext_init(si.mcontext);
dr_mcontext_init(si.raw_mcontext);
/* i#207: fragment tag and fcache start pc on fault. */
si.fault_fragment_info.tag = NULL;
si.fault_fragment_info.cache_start_pc = NULL;
/* i#182/PR 449996: we provide the pre-translation context */
if (raw_sc != NULL) {
fragment_t wrapper;
si.raw_mcontext_valid = true;
sigcontext_to_mcontext(dr_mcontext_as_priv_mcontext(si.raw_mcontext),
&raw_sc_full, si.raw_mcontext->flags);
/* i#207: fragment tag and fcache start pc on fault. */
/* FIXME: we should avoid the fragment_pclookup since it is expensive
* and since we already did the work of a lookup when translating
*/
if (fragment == NULL)
fragment = fragment_pclookup(dcontext, si.raw_mcontext->pc, &wrapper);
if (fragment != NULL && !hide_tag_from_client(fragment->tag)) {
si.fault_fragment_info.tag = fragment->tag;
si.fault_fragment_info.cache_start_pc = FCACHE_ENTRY_PC(fragment);
si.fault_fragment_info.is_trace = TEST(FRAG_IS_TRACE, fragment->flags);
si.fault_fragment_info.app_code_consistent =
!TESTANY(FRAG_WAS_DELETED | FRAG_SELFMOD_SANDBOXED, fragment->flags);
}
} else
si.raw_mcontext_valid = false;
/* The client has no way to calculate this when using
* instrumentation that deliberately faults (to shift a rare event
* out of the fastpath) so we provide it. When raw_mcontext is
* available the client can calculate it, but we provide it as a
* convenience anyway.
*/
si.access_address = access_address;
si.blocked = blocked;
ucontext_to_mcontext(dr_mcontext_as_priv_mcontext(si.mcontext), uc);
/* We disallow the client calling dr_redirect_execution(), so we
* will not leak si
*/
action = instrument_signal(dcontext, &si);
if (action == DR_SIGNAL_DELIVER || action == DR_SIGNAL_REDIRECT) {
/* propagate client changes */
CLIENT_ASSERT(si.mcontext->flags == DR_MC_ALL,
"signal mcontext flags cannot be changed");
mcontext_to_ucontext(uc, dr_mcontext_as_priv_mcontext(si.mcontext));
} else if (action == DR_SIGNAL_SUPPRESS && raw_sc != NULL) {
/* propagate client changes */
CLIENT_ASSERT(si.raw_mcontext->flags == DR_MC_ALL,
"signal mcontext flags cannot be changed");
mcontext_to_sigcontext(&raw_sc_full,
dr_mcontext_as_priv_mcontext(si.raw_mcontext),
si.raw_mcontext->flags);
}
heap_free(dcontext, si.mcontext, sizeof(*si.mcontext) HEAPACCT(ACCT_OTHER));
heap_free(dcontext, si.raw_mcontext, sizeof(*si.raw_mcontext) HEAPACCT(ACCT_OTHER));
return action;
}
/* Returns false if caller should exit */
static bool
handle_client_action_from_cache(dcontext_t *dcontext, int sig, dr_signal_action_t action,
sigframe_rt_t *our_frame, sigcontext_t *sc_orig,
sigcontext_t *sc_interrupted, bool blocked)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
kernel_ucontext_t *uc = get_ucontext_from_rt_frame(our_frame);
sigcontext_t *sc = SIGCXT_FROM_UCXT(uc);
/* in order to pass to the client, we come all the way here for signals
* the app has no handler for
*/
if (action == DR_SIGNAL_REDIRECT) {
/* send_signal_to_client copied mcontext into our
* master_signal_handler frame, so we set up for fcache_return w/
* our frame's state
*/
transfer_from_sig_handler_to_fcache_return(
dcontext, uc, sc_interrupted, sig, (app_pc)sc->SC_XIP,
(linkstub_t *)get_asynch_linkstub(), true);
if (is_building_trace(dcontext)) {
LOG(THREAD, LOG_ASYNCH, 3, "\tsquashing trace-in-progress\n");
trace_abort(dcontext);
}
return false;
} else if (action == DR_SIGNAL_SUPPRESS ||
(!blocked && info->app_sigaction[sig] != NULL &&
info->app_sigaction[sig]->handler == (handler_t)SIG_IGN)) {
LOG(THREAD, LOG_ASYNCH, 2, "%s: not delivering!\n",
(action == DR_SIGNAL_SUPPRESS) ? "client suppressing signal"
: "app signal handler is SIG_IGN");
/* Restore original (untranslated) sc. */
restore_orig_sigcontext(our_frame, sc_orig);
return false;
} else if (!blocked && /* no BYPASS for blocked */
(action == DR_SIGNAL_BYPASS ||
(info->app_sigaction[sig] == NULL ||
info->app_sigaction[sig]->handler == (handler_t)SIG_DFL))) {
LOG(THREAD, LOG_ASYNCH, 2, "%s: executing default action\n",
(action == DR_SIGNAL_BYPASS) ? "client forcing default"
: "app signal handler is SIG_DFL");
if (execute_default_from_cache(dcontext, sig, our_frame, sc_orig, false)) {
/* If we haven't terminated, restore original (untranslated) sc
* on request.
*/
restore_orig_sigcontext(our_frame, sc_orig);
LOG(THREAD, LOG_ASYNCH, 2, "%s: restored xsp=" PFX ", xip=" PFX "\n",
__FUNCTION__, get_sigcontext_from_rt_frame(our_frame)->SC_XSP,
get_sigcontext_from_rt_frame(our_frame)->SC_XIP);
}
return false;
}
CLIENT_ASSERT(action == DR_SIGNAL_DELIVER, "invalid signal event return value");
return true;
}
#endif
static void
abort_on_fault(dcontext_t *dcontext, uint dumpcore_flag, app_pc pc, byte *target, int sig,
sigframe_rt_t *frame, const char *prefix, const char *signame,
const char *where)
{
kernel_ucontext_t *ucxt = &frame->uc;
sigcontext_t *sc = SIGCXT_FROM_UCXT(ucxt);
bool stack_overflow = (sig == SIGSEGV && is_stack_overflow(dcontext, target));
#if defined(STATIC_LIBRARY) && defined(LINUX)
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
uint orig_dumpcore_flag = dumpcore_flag;
if (init_info.app_sigaction != NULL)
info = &init_info; /* use init-time handler */
ASSERT(info->app_sigaction != NULL);
#endif
const char *fmt = "%s %s at PC " PFX "\n"
"Received SIG%s at%s pc " PFX " in thread " TIDFMT "\n"
"Base: " PFX "\n"
"Registers:"
#ifdef X86
"eax=" PFX " ebx=" PFX " ecx=" PFX " edx=" PFX "\n"
"\tesi=" PFX " edi=" PFX " esp=" PFX " ebp=" PFX "\n"
# ifdef X64
"\tr8 =" PFX " r9 =" PFX " r10=" PFX " r11=" PFX "\n"
"\tr12=" PFX " r13=" PFX " r14=" PFX " r15=" PFX "\n"
# endif /* X64 */
#elif defined(ARM)
# ifndef X64
" r0 =" PFX " r1 =" PFX " r2 =" PFX " r3 =" PFX "\n"
"\tr4 =" PFX " r5 =" PFX " r6 =" PFX " r7 =" PFX "\n"
"\tr8 =" PFX " r9 =" PFX " r10=" PFX " r11=" PFX "\n"
"\tr12=" PFX " r13=" PFX " r14=" PFX " r15=" PFX "\n"
# else
# error NYI on AArch64
# endif
#endif /* X86/ARM */
"\teflags=" PFX;
#if defined(STATIC_LIBRARY) && defined(LINUX)
/* i#2119: if we're invoking an app handler, disable a fatal coredump. */
if (INTERNAL_OPTION(invoke_app_on_crash) && info->app_sigaction[sig] != NULL &&
IS_RT_FOR_APP(info, sig) && TEST(dumpcore_flag, DYNAMO_OPTION(dumpcore_mask)) &&
!DYNAMO_OPTION(live_dump))
dumpcore_flag = 0;
#endif
report_dynamorio_problem(
dcontext, dumpcore_flag | (stack_overflow ? DUMPCORE_STACK_OVERFLOW : 0), pc,
(app_pc)sc->SC_FP, fmt, prefix, stack_overflow ? STACK_OVERFLOW_NAME : CRASH_NAME,
pc, signame, where, pc, d_r_get_thread_id(), get_dynamorio_dll_start(),
#ifdef X86
sc->SC_XAX, sc->SC_XBX, sc->SC_XCX, sc->SC_XDX, sc->SC_XSI, sc->SC_XDI,
sc->SC_XSP, sc->SC_XBP,
# ifdef X64
sc->SC_FIELD(r8), sc->SC_FIELD(r9), sc->SC_FIELD(r10), sc->SC_FIELD(r11),
sc->SC_FIELD(r12), sc->SC_FIELD(r13), sc->SC_FIELD(r14), sc->SC_FIELD(r15),
# endif /* X86 */
#elif defined(ARM)
# ifndef X64
sc->SC_FIELD(arm_r0), sc->SC_FIELD(arm_r1), sc->SC_FIELD(arm_r2),
sc->SC_FIELD(arm_r3), sc->SC_FIELD(arm_r4), sc->SC_FIELD(arm_r5),
sc->SC_FIELD(arm_r6), sc->SC_FIELD(arm_r7), sc->SC_FIELD(arm_r8),
sc->SC_FIELD(arm_r9), sc->SC_FIELD(arm_r10), sc->SC_FIELD(arm_fp),
sc->SC_FIELD(arm_ip), sc->SC_FIELD(arm_sp), sc->SC_FIELD(arm_lr),
sc->SC_FIELD(arm_pc),
# else
# error NYI on AArch64
# endif /* X64 */
#endif /* X86/ARM */
sc->SC_XFLAGS);
#if defined(STATIC_LIBRARY) && defined(LINUX)
/* i#2119: For static DR, the surrounding app's handler may well be
* safe to invoke even when DR state is messed up: it's worth a try, as it
* likely has useful reporting features for users of the app.
* We limit to Linux and RT for simplicity: it can be expanded later if static
* library use expands.
*/
if (INTERNAL_OPTION(invoke_app_on_crash) && info->app_sigaction[sig] != NULL &&
IS_RT_FOR_APP(info, sig)) {
SYSLOG(SYSLOG_WARNING, INVOKING_APP_HANDLER, 2, get_application_name(),
get_application_pid());
(*info->app_sigaction[sig]->handler)(sig, &frame->info, ucxt);
/* If the app handler didn't terminate, now get a fatal core. */
if (TEST(orig_dumpcore_flag, DYNAMO_OPTION(dumpcore_mask)) &&
!DYNAMO_OPTION(live_dump))
os_dump_core("post-app-handler attempt at core dump");
}
#endif
os_terminate(dcontext, TERMINATE_PROCESS);
ASSERT_NOT_REACHED();
}
static void
abort_on_DR_fault(dcontext_t *dcontext, app_pc pc, byte *target, int sig,
sigframe_rt_t *frame, const char *signame, const char *where)
{
abort_on_fault(dcontext, DUMPCORE_INTERNAL_EXCEPTION, pc, target, sig, frame,
exception_label_core, signame, where);
ASSERT_NOT_REACHED();
}
/* Returns whether unlinked or mangled syscall.
* Restored in receive_pending_signal.
*/
static bool
unlink_fragment_for_signal(dcontext_t *dcontext, fragment_t *f,
byte *pc /*interruption pc*/)
{
/* We only come here if we interrupted a fragment in the cache,
* or interrupted transition gencode (i#2019),
* which means that this thread's DR state is safe, and so it
* should be ok to acquire a lock. xref PR 596069.
*
* There is a race where if two threads hit a signal in the same
* shared fragment, the first could re-link after the second
* un-links but before the second exits, and the second could then
* execute the syscall, resulting in arbitrary delay prior to
* signal delivery. We don't want to allocate global memory,
* but we could use a static array of counters (since should
* be small # of interrupted shared fragments at any one time)
* used as refcounts so we only unlink when all are done.
* Not bothering to implement now: going to live w/ chance of
* long signal delays. xref PR 596069.
*/
bool changed = false;
bool waslinking = is_couldbelinking(dcontext);
if (!waslinking)
enter_couldbelinking(dcontext, NULL, false);
/* may not be linked if trace_relink or something */
if (TEST(FRAG_COARSE_GRAIN, f->flags)) {
/* XXX PR 213040: we don't support unlinking coarse, so we try
* not to come here, but for indirect branch and other spots
* where we don't yet support translation (since can't fault)
* we end up w/ no bound on delivery...
*/
} else if (TEST(FRAG_LINKED_OUTGOING, f->flags)) {
LOG(THREAD, LOG_ASYNCH, 3, "\tunlinking outgoing for interrupted F%d\n", f->id);
SHARED_FLAGS_RECURSIVE_LOCK(f->flags, acquire, change_linking_lock);
// Double-check flags to ensure some other thread didn't unlink
// while we waited for the change_linking_lock.
if (TEST(FRAG_LINKED_OUTGOING, f->flags)) {
unlink_fragment_outgoing(dcontext, f);
changed = true;
}
SHARED_FLAGS_RECURSIVE_LOCK(f->flags, release, change_linking_lock);
} else {
LOG(THREAD, LOG_ASYNCH, 3, "\toutgoing already unlinked for interrupted F%d\n",
f->id);
}
if (TEST(FRAG_HAS_SYSCALL, f->flags)) {
/* Syscalls are signal barriers!
* Make sure the next syscall (if any) in f is not executed!
* instead go back to d_r_dispatch right before the syscall
*/
/* syscall mangling does a bunch of decodes but only one write,
* changing the target of a short jmp, which is atomic
* since a one-byte write, so we don't need the change_linking_lock.
*/
if (mangle_syscall_code(dcontext, f, pc, false /*do not skip exit cti*/))
changed = true;
}
if (!waslinking)
enter_nolinking(dcontext, NULL, false);
return changed;
}
static void
relink_interrupted_fragment(dcontext_t *dcontext, thread_sig_info_t *info)
{
if (info->interrupted == NULL)
return;
/* i#2066: if we were building a trace, it may already be re-linked */
if (!TEST(FRAG_LINKED_OUTGOING, info->interrupted->flags)) {
LOG(THREAD, LOG_ASYNCH, 3, "\tre-linking outgoing for interrupted F%d\n",
info->interrupted->id);
SHARED_FLAGS_RECURSIVE_LOCK(info->interrupted->flags, acquire,
change_linking_lock);
/* Double-check flags to ensure some other thread didn't link
* while we waited for the change_linking_lock.
*/
if (!TEST(FRAG_LINKED_OUTGOING, info->interrupted->flags)) {
link_fragment_outgoing(dcontext, info->interrupted, false);
}
SHARED_FLAGS_RECURSIVE_LOCK(info->interrupted->flags, release,
change_linking_lock);
}
if (TEST(FRAG_HAS_SYSCALL, info->interrupted->flags)) {
/* restore syscall (they're a barrier to signals, so signal
* handler has cur frag exit before it does a syscall)
*/
if (info->interrupted_pc != NULL) {
mangle_syscall_code(dcontext, info->interrupted, info->interrupted_pc,
true /*skip exit cti*/);
}
}
info->interrupted = NULL;
info->interrupted_pc = NULL;
}
static bool
interrupted_inlined_syscall(dcontext_t *dcontext, fragment_t *f,
byte *pc /*interruption pc*/)
{
bool pre_or_post_syscall = false;
if (TEST(FRAG_HAS_SYSCALL, f->flags)) {
/* PR 596147: if the thread is currently in an inlined
* syscall when a signal comes in, we can't delay and bound the
* delivery time: we need to deliver now. Should decode
* backward and see if syscall. We assume our translation of
* the interruption state is fine to re-start: i.e., the syscall
* is complete if kernel has pc at post-syscall point, and
* kernel set EINTR in eax if necessary.
*/
/* Interrupted fcache, so ok to alloc memory for decode */
instr_t instr;
byte *nxt_pc;
instr_init(dcontext, &instr);
nxt_pc = decode(dcontext, pc, &instr);
if (nxt_pc != NULL && instr_valid(&instr) && instr_is_syscall(&instr)) {
/* pre-syscall but post-jmp so can't skip syscall */
pre_or_post_syscall = true;
} else {
size_t syslen = syscall_instr_length(FRAG_ISA_MODE(f->flags));
instr_reset(dcontext, &instr);
nxt_pc = decode(dcontext, pc - syslen, &instr);
if (nxt_pc != NULL && instr_valid(&instr) && instr_is_syscall(&instr)) {
#if defined(X86) && !defined(MACOS)
/* decoding backward so check for exit cti jmp prior
* to syscall to ensure no mismatch
*/
instr_reset(dcontext, &instr);
nxt_pc = decode(dcontext, pc - syslen - JMP_LONG_LENGTH, &instr);
if (nxt_pc != NULL && instr_valid(&instr) &&
instr_get_opcode(&instr) == OP_jmp) {
/* post-inlined-syscall */
pre_or_post_syscall = true;
}
#else
/* On Mac and ARM we have some TLS spills in between so we just
* trust that this is a syscall (esp on ARM w/ aligned instrs).
*/
pre_or_post_syscall = true;
#endif
}
}
instr_free(dcontext, &instr);
}
return pre_or_post_syscall;
}
/* i#1145: auto-restart syscalls interrupted by signals */
static bool
adjust_syscall_for_restart(dcontext_t *dcontext, thread_sig_info_t *info, int sig,
sigcontext_t *sc, fragment_t *f, reg_t orig_retval_reg)
{
byte *pc = (byte *)sc->SC_XIP;
int sys_inst_len;
if (sc->IF_X86_ELSE(SC_XAX, SC_R0) != -EINTR) {
/* The syscall succeeded, so no reason to interrupt.
* Some syscalls succeed on a signal coming in.
* E.g., SYS_wait4 on SIGCHLD, or reading from a slow device.
* XXX: Now that we pass SA_RESTART we should never get here?
*/
return false;
}
/* Don't restart if the app's handler says not to */
if (info->app_sigaction[sig] != NULL &&
!TEST(SA_RESTART, info->app_sigaction[sig]->flags)) {
return false;
}
/* XXX i#1145: some syscalls are never restarted when interrupted by a signal.
* We check those that are simple to distinguish below, but not all are. We have
* this under an option so it can be disabled if necessary.
*/
if (!DYNAMO_OPTION(restart_syscalls))
return false;
/* Now that we use SA_RESTART we rely on that and ignore our own
* inaccurate check sysnum_is_not_restartable(sysnum).
* SA_RESTART also means we can just be passed in the register value to restore.
*/
LOG(THREAD, LOG_ASYNCH, 2, "%s: restored xax/r0 to %ld\n", __FUNCTION__,
orig_retval_reg);
#ifdef X86
sc->SC_XAX = orig_retval_reg;
#elif defined(AARCHXX)
sc->SC_R0 = orig_retval_reg;
#else
# error NYI
#endif
/* Now adjust the pc to point at the syscall instruction instead of after it,
* so when we resume we'll go back to the syscall.
* Adjusting solves transparency as well: natively the kernel adjusts
* the pc before setting up the signal frame.
* We don't pass in the post-syscall pc provided by the kernel because
* we want the app pc, not the raw pc.
*/
dr_isa_mode_t isa_mode;
if (is_after_syscall_address(dcontext, pc) || pc == vsyscall_sysenter_return_pc) {
isa_mode = dr_get_isa_mode(dcontext);
} else {
/* We're going to walk back in the fragment, not gencode */
ASSERT(f != NULL);
isa_mode = FRAG_ISA_MODE(f->flags);
}
sys_inst_len = syscall_instr_length(isa_mode);
if (pc == vsyscall_sysenter_return_pc) {
#ifdef X86
sc->SC_XIP = (ptr_uint_t)(vsyscall_syscall_end_pc - sys_inst_len);
/* To restart sysenter we must re-copy xsp into xbp, as xbp is
* clobbered by the kernel.
* XXX: The kernel points at the int 0x80 in vsyscall on a restart
* and so doesn't have to do this: should we do that too? If so we'll
* have to avoid interpreting our own hook which is right after the
* int 0x80.
*/
sc->SC_XBP = sc->SC_XSP;
#else
ASSERT_NOT_REACHED();
#endif
} else if (is_after_syscall_address(dcontext, pc)) {
/* We're at do_syscall: point at app syscall instr. We want an app
* address b/c this signal will be delayed and the delivery will use
* a direct app context: no translation from the cache.
* The caller sets info->sigpending[sig]->use_sigcontext for us.
*/
sc->SC_XIP = (ptr_uint_t)(dcontext->asynch_target - sys_inst_len);
DODEBUG({
instr_t instr;
dr_isa_mode_t old_mode;
dr_set_isa_mode(dcontext, isa_mode, &old_mode);
instr_init(dcontext, &instr);
ASSERT(decode(dcontext, (app_pc)sc->SC_XIP, &instr) != NULL &&
instr_is_syscall(&instr));
instr_free(dcontext, &instr);
dr_set_isa_mode(dcontext, old_mode, NULL);
});
} else {
ASSERT_NOT_REACHED(); /* Inlined syscalls no longer come here. */
}
#ifdef AARCHXX
/* dr_reg_stolen is holding DR's TLS on receiving a signal,
* so we need to put the app's reg value into the ucontext instead.
* The translation process normally does this for us, but here we're doing
* a custom translation.
*/
set_sigcxt_stolen_reg(sc, dcontext->local_state->spill_space.reg_stolen);
#endif
LOG(THREAD, LOG_ASYNCH, 2, "%s: sigreturn pc is now " PFX "\n", __FUNCTION__,
sc->SC_XIP);
return true;
}
/* XXX: Better to get this code inside arch/ but we'd have to convert to an mcontext
* which seems overkill.
*/
static fragment_t *
find_next_fragment_from_gencode(dcontext_t *dcontext, sigcontext_t *sc)
{
fragment_t *f = NULL;
fragment_t wrapper;
byte *pc = (byte *)sc->SC_XIP;
if (in_clean_call_save(dcontext, pc) || in_clean_call_restore(dcontext, pc)) {
#ifdef AARCHXX
f = fragment_pclookup(dcontext, (cache_pc)sc->SC_LR, &wrapper);
#elif defined(X86)
cache_pc retaddr = NULL;
/* Get the retaddr. We assume this is the adjustment used by
* insert_out_of_line_context_switch().
*/
byte *ra_slot =
dcontext->dstack - get_clean_call_switch_stack_size() - sizeof(retaddr);
/* The extra x86 slot is only there for save. */
if (in_clean_call_save(dcontext, pc))
ra_slot -= get_clean_call_temp_stack_size();
if (d_r_safe_read(ra_slot, sizeof(retaddr), &retaddr))
f = fragment_pclookup(dcontext, retaddr, &wrapper);
#else
# error Unsupported arch.
#endif
} else if (in_indirect_branch_lookup_code(dcontext, pc)) {
/* Try to find the target if the signal arrived in the IBL.
* We could try to be a lot more precise by hardcoding the IBL
* sequence here but that would make the code less maintainable.
* Instead we try the registers that hold the target app address.
*/
/* First check for the jmp* on the hit path: that is the only place
* in the ibl where the target tag is not sitting in a register.
*/
#if defined(X86) && defined(X64)
/* Optimization for the common case of targeting a prefix on x86_64:
* ff 61 08 jmp 0x08(%rcx)[8byte]
* The tag is in 0x0(%rcx) so we avoid a decode and pclookup.
*/
if (*pc == 0xff && *(pc + 1) == 0x61 && *(pc + 2) == 0x08) {
f = fragment_lookup(dcontext, *(app_pc *)sc->SC_XCX);
}
#endif
if (f == NULL) {
instr_t instr;
instr_init(dcontext, &instr);
decode_cti(dcontext, pc, &instr);
if (instr_is_ibl_hit_jump(&instr)) {
priv_mcontext_t mc;
sig_full_cxt_t sc_full = { sc, NULL /*not provided*/ };
sigcontext_to_mcontext(&mc, &sc_full, DR_MC_INTEGER | DR_MC_CONTROL);
byte *target;
if (opnd_is_memory_reference(instr_get_target(&instr))) {
target = instr_compute_address_priv(&instr, &mc);
ASSERT(target != NULL);
if (target != NULL)
target = *(byte **)target;
} else {
ASSERT(opnd_is_reg(instr_get_target(&instr)));
target = (byte *)reg_get_value_priv(
opnd_get_reg(instr_get_target(&instr)), &mc);
}
ASSERT(target != NULL);
if (target != NULL)
f = fragment_pclookup(dcontext, target, &wrapper);
/* I tried to hit this case running client.cleancallsig in a loop
* and while I could on x86 and x86_64 I never did on ARM or
* AArch64. We can remove this once someone hits it and it works.
*/
IF_AARCHXX(ASSERT_NOT_TESTED());
}
instr_free(dcontext, &instr);
}
#ifdef AARCHXX
/* The target is in r2 the whole time, w/ or w/o Thumb LSB. */
if (f == NULL && sc->SC_R2 != 0)
f = fragment_lookup(dcontext, ENTRY_PC_TO_DECODE_PC(sc->SC_R2));
#elif defined(X86)
/* The target is initially in xcx but is then copied to xbx. */
if (f == NULL && sc->SC_XBX != 0)
f = fragment_lookup(dcontext, (app_pc)sc->SC_XBX);
if (f == NULL && sc->SC_XCX != 0)
f = fragment_lookup(dcontext, (app_pc)sc->SC_XCX);
#else
# error Unsupported arch.
#endif
} else {
/* If in fcache_enter or do_syscall*, we stored the next_tag in asynch_target
* in d_r_dispatch. But, we need to avoid using the asynch_target for the
* fragment we just exited if we're in fcache_return.
*/
if (dcontext->asynch_target != NULL && !in_fcache_return(dcontext, pc))
f = fragment_lookup(dcontext, dcontext->asynch_target);
}
return f;
}
static void
copy_sigcontext(sigcontext_t *dst_sc, void *dst_fpstate_opaque, sigcontext_t *src_sc)
{
ASSERT(dst_sc != NULL && src_sc != NULL);
*dst_sc = *src_sc;
#if defined(X86) && defined(LINUX)
kernel_fpstate_t *dst_fpstate = (kernel_fpstate_t *)dst_fpstate_opaque;
ASSERT(dst_fpstate != NULL);
if (src_sc->fpstate != NULL) {
*dst_fpstate = *src_sc->fpstate;
dst_sc->fpstate = dst_fpstate;
}
#endif
}
static void
record_pending_signal(dcontext_t *dcontext, int sig, kernel_ucontext_t *ucxt,
sigframe_rt_t *frame, bool forged _IF_CLIENT(byte *access_address))
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
os_thread_data_t *ostd = (os_thread_data_t *)dcontext->os_field;
sigcontext_t *sc = SIGCXT_FROM_UCXT(ucxt);
/* We need a fpstate to store original pre-xl8 SIMD values. */
#ifdef X86
kernel_fpstate_t fpstate_orig;
#endif
sigcontext_t sc_orig;
byte *pc = (byte *)sc->SC_XIP;
byte *xsp = (byte *)sc->SC_XSP;
bool receive_now = false;
bool blocked = false;
bool handled = false;
bool at_auto_restart_syscall = false;
int syslen = 0;
reg_t orig_retval_reg = sc->IF_X86_ELSE(SC_XAX, SC_R0);
sigpending_t *pend;
fragment_t *f = NULL;
fragment_t wrapper;
/* We no longer block SUSPEND_SIGNAL (i#184/PR 450670) or SIGSEGV (i#193/PR 287309).
* But we can have re-entrancy issues in this routine if the app uses the same
* SUSPEND_SIGNAL, or the nested SIGSEGV needs to be sent to the app. The
* latter shouldn't happen unless the app sends SIGSEGV via SYS_kill().
*/
if (ostd->processing_signal > 0 ||
/* If we interrupted receive_pending_signal() we can't prepend a new
* pending or delete an old b/c we might mess up the state so we
* just drop this one: should only happen for alarm signal
*/
(((info->accessing_sigpending && !info->nested_pending_ok) ||
/* It's risky to deliver to an exiting thread: data structs are in unstable
* states (i#4438). Just drop if we can.
*/
dcontext->is_exiting) &&
/* we do want to report a crash in receive_pending_signal() */
(can_always_delay[sig] ||
is_sys_kill(dcontext, pc, (byte *)sc->SC_XSP, &frame->info)))) {
LOG(THREAD, LOG_ASYNCH, 1, "nested or exit-time signal %d\n", sig);
ASSERT(ostd->processing_signal == 0 || sig == SUSPEND_SIGNAL || sig == SIGSEGV);
ASSERT(can_always_delay[sig] ||
is_sys_kill(dcontext, pc, (byte *)sc->SC_XSP, &frame->info));
/* To avoid re-entrant execution of special_heap_alloc() and of
* prepending to the pending list we just drop this signal.
* FIXME i#194/PR 453996: do better.
*/
STATS_INC(num_signals_dropped);
SYSLOG_INTERNAL_WARNING_ONCE("dropping nested/exit-time signal");
return;
}
ostd->processing_signal++; /* no need for atomicity: thread-private */
/* First, check whether blocked, before we restore for sigsuspend (i#1340). */
if (kernel_sigismember(&info->app_sigblocked, sig))
blocked = true;
if (info->in_sigsuspend) {
/* sigsuspend ends when a signal is received, so restore the
* old blocked set
*/
info->app_sigblocked = info->app_sigblocked_save;
info->in_sigsuspend = false;
/* update the set to restore to post-signal-delivery */
#ifdef MACOS
ucxt->uc_sigmask = *(__darwin_sigset_t *)&info->app_sigblocked;
#else
ucxt->uc_sigmask = info->app_sigblocked;
#endif
#ifdef DEBUG
if (d_r_stats->loglevel >= 3 && (d_r_stats->logmask & LOG_ASYNCH) != 0) {
LOG(THREAD, LOG_ASYNCH, 3, "after sigsuspend, blocked signals are now:\n");
dump_sigset(dcontext, &info->app_sigblocked);
}
#endif
}
if (get_at_syscall(dcontext))
syslen = syscall_instr_length(dr_get_isa_mode(dcontext));
if (info->app_sigaction[sig] != NULL &&
info->app_sigaction[sig]->handler ==
(handler_t)SIG_IGN
/* If a client registered a handler, put this in the queue.
* Races between registering, queueing, and delivering are fine.
*/
IF_CLIENT_INTERFACE(&&!dr_signal_hook_exists())) {
LOG(THREAD, LOG_ASYNCH, 3,
"record_pending_signal (%d at pc " PFX "): action is SIG_IGN!\n", sig, pc);
ostd->processing_signal--;
return;
} else if (blocked) {
/* signal is blocked by app, so just record it, don't receive now */
LOG(THREAD, LOG_ASYNCH, 2,
"record_pending_signal(%d at pc " PFX "): signal is currently blocked\n", sig,
pc);
IF_LINUX(handled = notify_signalfd(dcontext, info, sig, frame));
} else if (dcontext->currently_stopped) {
/* We have no way to delay for a currently-native thread. */
LOG(THREAD, LOG_ASYNCH, 2, "Going to receive signal natively now\n");
execute_native_handler(dcontext, sig, frame);
handled = true;
} else if (safe_is_in_fcache(dcontext, pc, xsp)) {
LOG(THREAD, LOG_ASYNCH, 2, "record_pending_signal(%d) from cache pc " PFX "\n",
sig, pc);
if (forged || can_always_delay[sig]) {
/* to make translation easier, want to delay if can until d_r_dispatch
* unlink cur frag, wait for d_r_dispatch
*/
/* check for coarse first to avoid cost of coarse pclookup */
if (get_fcache_coarse_info(pc) != NULL) {
/* PR 213040: we can't unlink coarse. If we fail to translate
* we'll switch back to delaying, below.
*/
if (sig_is_alarm_signal(sig) && info->sigpending[sig] != NULL &&
info->sigpending[sig]->next != NULL && info->skip_alarm_xl8 > 0) {
/* Translating coarse fragments is very expensive so we
* avoid doing it when we're having trouble keeping up w/
* the alarm frequency (PR 213040), but we make sure we try
* every once in a while to avoid unbounded signal delay
*/
info->skip_alarm_xl8--;
STATS_INC(num_signals_coarse_delayed);
} else {
if (sig_is_alarm_signal(sig))
info->skip_alarm_xl8 = SKIP_ALARM_XL8_MAX;
receive_now = true;
LOG(THREAD, LOG_ASYNCH, 2,
"signal interrupted coarse fragment so delivering now\n");
}
} else {
f = fragment_pclookup(dcontext, pc, &wrapper);
ASSERT(f != NULL);
ASSERT(!TEST(FRAG_COARSE_GRAIN, f->flags)); /* checked above */
LOG(THREAD, LOG_ASYNCH, 2, "\tdelaying until exit F%d\n", f->id);
if (interrupted_inlined_syscall(dcontext, f, pc)) {
/* PR 596147: if delayable signal arrives after syscall-skipping
* jmp, either at syscall or post-syscall, we deliver
* immediately, since we can't bound the delay
*/
receive_now = true;
LOG(THREAD, LOG_ASYNCH, 2,
"signal interrupted pre/post syscall itself so delivering now\n");
/* We don't set at_auto_restart_syscall because we just leave
* the SA_RESTART kernel-supplied resumption point: with no
* post-syscall handler to worry about we have no need to
* change anything.
*/
} else {
/* could get another signal but should be in same fragment */
ASSERT(info->interrupted == NULL || info->interrupted == f);
if (info->interrupted != f) {
/* Just in case there's a prior, avoid leaving it unlinked. */
relink_interrupted_fragment(dcontext, info);
if (unlink_fragment_for_signal(dcontext, f, pc)) {
info->interrupted = f;
info->interrupted_pc = pc;
} else {
/* either was unlinked for trace creation, or we got another
* signal before exiting cache to handle 1st
*/
ASSERT(info->interrupted == NULL || info->interrupted == f);
}
}
}
}
} else {
/* the signal interrupted code cache => run handler now! */
receive_now = true;
LOG(THREAD, LOG_ASYNCH, 2, "\tnot certain can delay so handling now\n");
}
} else if (in_generated_routine(dcontext, pc) ||
/* XXX: should also check fine stubs */
safe_is_in_coarse_stubs(dcontext, pc, xsp)) {
/* Assumption: dynamo errors have been caught already inside
* the master_signal_handler, thus any error in a generated routine
* is an asynch signal that can be delayed
*/
LOG(THREAD, LOG_ASYNCH, 2,
"record_pending_signal(%d) from gen routine or stub " PFX "\n", sig, pc);
if (get_at_syscall(dcontext)) {
/* i#1206: the syscall was interrupted, so we can go back to d_r_dispatch
* and don't need to receive it now (which complicates post-syscall handling)
* w/o any extra delay.
*/
/* i#2659: we now use SA_RESTART to handle interrupting native
* auto-restart syscalls. That means we have to adjust do_syscall
* interruption to give us control so we can deliver the signal. Due to
* needing to run post-syscall handlers (we don't want to get into nested
* dcontexts like on Windows) it's simplest to go back to d_r_dispatch, which
* is most easily done by emulating the non-SA_RESTART behavior.
* XXX: This all seems backward: we should revisit this model and see if
* we can get rid of this emulation and the auto-restart emulation.
*/
/* The get_at_syscall() check above distinguishes from just having
* arrived at the syscall instr, but with SA_RESTART we can't distinguish
* not-yet-executed-syscall from syscall-was-interrupted-in-the-kernel.
* This matters for sigreturn (i#2995), whose asynch_target points somewhere
* other than right after the syscall, so we exclude it (it can't be
* interrupted so we know we haven't executed it yet).
*/
if (is_after_syscall_address(dcontext, pc + syslen) &&
!is_sigreturn_syscall_number(sc->SC_SYSNUM_REG)) {
LOG(THREAD, LOG_ASYNCH, 2,
"Adjusting interrupted auto-restart syscall from " PFX " to " PFX
"\n",
pc, pc + syslen);
at_auto_restart_syscall = true;
sc->SC_XIP += syslen;
sc->IF_X86_ELSE(SC_XAX, SC_R0) = -EINTR;
pc = (byte *)sc->SC_XIP;
}
}
/* This could come from another thread's SYS_kill (via our gen do_syscall) */
DOLOG(1, LOG_ASYNCH, {
if (!is_after_syscall_address(dcontext, pc) && !forged &&
!can_always_delay[sig]) {
LOG(THREAD, LOG_ASYNCH, 1,
"WARNING: signal %d in gen routine: may cause problems!\n", sig);
}
});
/* i#2019: for a signal arriving in gencode before entry to a fragment,
* we need to unlink the fragment just like for a signal arriving inside
* the fragment itself.
* Multiple signals should all have the same asynch_target so we should
* only need a single info->interrupted.
*/
if (info->interrupted == NULL && !get_at_syscall(dcontext)) {
f = find_next_fragment_from_gencode(dcontext, sc);
if (f != NULL && !TEST(FRAG_COARSE_GRAIN, f->flags)) {
if (unlink_fragment_for_signal(dcontext, f, FCACHE_ENTRY_PC(f))) {
info->interrupted = f;
info->interrupted_pc = FCACHE_ENTRY_PC(f);
}
}
}
} else if (get_at_syscall(dcontext) && pc == vsyscall_sysenter_return_pc - syslen &&
/* See i#2995 comment above: rule out sigreturn */
!is_sigreturn_syscall_number(sc->SC_SYSNUM_REG)) {
LOG(THREAD, LOG_ASYNCH, 2,
"record_pending_signal(%d) from restart-vsyscall " PFX "\n", sig, pc);
/* While the kernel points at int 0x80 for a restart, we leverage our
* existing sysenter restart mechanism.
*/
at_auto_restart_syscall = true;
sc->SC_XIP = (reg_t)vsyscall_sysenter_return_pc;
sc->IF_X86_ELSE(SC_XAX, SC_R0) = -EINTR;
pc = (byte *)sc->SC_XIP;
} else if (pc == vsyscall_sysenter_return_pc) {
LOG(THREAD, LOG_ASYNCH, 2, "record_pending_signal(%d) from vsyscall " PFX "\n",
sig, pc);
/* i#1206: the syscall was interrupted but is not auto-restart, so we can go
* back to d_r_dispatch and don't need to receive it now (which complicates
* post-syscall handling)
*/
} else if (thread_synch_check_state(dcontext, THREAD_SYNCH_NO_LOCKS) &&
/* Avoid grabbing locks for xl8 while in a suspended state (i#3026). */
ksynch_get_value(&ostd->suspended) == 0) {
/* The signal interrupted DR or the client but it's at a safe spot so
* deliver it now.
*/
receive_now = true;
} else {
/* the signal interrupted DR itself => do not run handler now! */
LOG(THREAD, LOG_ASYNCH, 2, "record_pending_signal(%d) from DR at pc " PFX "\n",
sig, pc);
if (!forged && !can_always_delay[sig] &&
!is_sys_kill(dcontext, pc, (byte *)sc->SC_XSP, &frame->info)) {
/* i#195/PR 453964: don't re-execute if will just re-fault.
* Our checks for dstack, etc. in master_signal_handler should
* have accounted for everything
*/
ASSERT_NOT_REACHED();
abort_on_DR_fault(dcontext, pc, NULL, sig, frame,
(sig == SIGSEGV) ? "SEGV" : "other", " unknown");
}
}
LOG(THREAD, LOG_ASYNCH, 3, "\taction is not SIG_IGN\n");
#if defined(X86) && defined(LINUX)
LOG(THREAD, LOG_ASYNCH, 3, "\tretaddr = " PFX "\n",
frame->pretcode); /* pretcode has same offs for plain */
#endif
if (receive_now) {
/* we need to translate sc before we know whether client wants to
* suppress, so we need a backup copy
*/
bool xl8_success;
ASSERT(!at_auto_restart_syscall); /* only used for delayed delivery */
copy_sigcontext(
&sc_orig, IF_X86_ELSE(IF_LINUX_ELSE((void *)&fpstate_orig, NULL), NULL), sc);
ASSERT(!forged);
/* cache the fragment since pclookup is expensive for coarse (i#658) */
f = fragment_pclookup(dcontext, (cache_pc)sc->SC_XIP, &wrapper);
xl8_success = translate_sigcontext(dcontext, ucxt, !can_always_delay[sig], f);
if (can_always_delay[sig] && !xl8_success) {
/* delay: we expect this for coarse fragments if alarm arrives
* in middle of ind branch region or sthg (PR 213040)
*/
LOG(THREAD, LOG_ASYNCH, 2,
"signal is in un-translatable spot in coarse fragment: delaying\n");
receive_now = false;
}
}
if (receive_now) {
/* N.B.: since we abandon the old context for synchronous signals,
* we do not need to mark this fragment as FRAG_CANNOT_DELETE
*/
#ifdef DEBUG
if (d_r_stats->loglevel >= 2 && (d_r_stats->logmask & LOG_ASYNCH) != 0 &&
safe_is_in_fcache(dcontext, pc, xsp)) {
ASSERT(f != NULL);
LOG(THREAD, LOG_ASYNCH, 2, "Got signal at pc " PFX " in this fragment:\n",
pc);
disassemble_fragment(dcontext, f, false);
}
#endif
LOG(THREAD, LOG_ASYNCH, 2, "Going to receive signal now\n");
/* If we end up executing the default action, we'll go native
* since we translated the context. If there's a handler,
* we'll copy the context to the app stack and then adjust the
* original on our stack so we take over.
*/
execute_handler_from_cache(dcontext, sig, frame, &sc_orig,
f _IF_CLIENT(access_address));
} else if (!handled) {
#ifdef CLIENT_INTERFACE
/* i#182/PR 449996: must let client act on blocked non-delayable signals to
* handle instrumentation faults. Make sure we're at a safe spot: i.e.,
* only raise for in-cache faults. Checking forged and no-delay
* to avoid the in-cache check for delayable signals => safer.
*/
if (blocked && !forged && !can_always_delay[sig] &&
safe_is_in_fcache(dcontext, pc, xsp)) {
dr_signal_action_t action;
/* cache the fragment since pclookup is expensive for coarse (i#658) */
f = fragment_pclookup(dcontext, (cache_pc)sc->SC_XIP, &wrapper);
copy_sigcontext(&sc_orig,
IF_X86_ELSE(IF_LINUX_ELSE((void *)&fpstate_orig, NULL), NULL),
sc);
translate_sigcontext(dcontext, ucxt, true /*shouldn't fail*/, f);
/* make a copy before send_signal_to_client() tweaks it */
sigcontext_t sc_interrupted = *sc;
action = send_signal_to_client(dcontext, sig, frame, &sc_orig, access_address,
true /*blocked*/, f);
/* For blocked signal early event we disallow BYPASS (xref i#182/PR 449996) */
CLIENT_ASSERT(action != DR_SIGNAL_BYPASS,
"cannot bypass a blocked signal event");
if (!handle_client_action_from_cache(dcontext, sig, action, frame, &sc_orig,
&sc_interrupted, true /*blocked*/)) {
ostd->processing_signal--;
return;
}
/* Restore original (untranslated) sc. */
restore_orig_sigcontext(frame, &sc_orig);
}
#endif
/* i#196/PR 453847: avoid infinite loop of signals if try to re-execute */
if (blocked && !can_always_delay[sig] &&
!is_sys_kill(dcontext, pc, (byte *)sc->SC_XSP, &frame->info)) {
ASSERT(default_action[sig] == DEFAULT_TERMINATE ||
default_action[sig] == DEFAULT_TERMINATE_CORE);
LOG(THREAD, LOG_ASYNCH, 1,
"blocked fatal signal %d cannot be delayed: terminating\n", sig);
copy_sigcontext(&sc_orig,
IF_X86_ELSE(IF_LINUX_ELSE((void *)&fpstate_orig, NULL), NULL),
sc);
/* If forged we're likely couldbelinking, and we don't need to xl8. */
if (forged)
ASSERT(is_couldbelinking(dcontext));
else
translate_sigcontext(dcontext, ucxt, true /*shouldn't fail*/, NULL);
/* the process should be terminated */
execute_default_from_cache(dcontext, sig, frame, &sc_orig, forged);
ASSERT_NOT_REACHED();
}
/* Happened in DR, do not translate context. Record for later processing
* at a safe point with a clean app state.
*/
if (!blocked || sig >= OFFS_RT || (blocked && info->sigpending[sig] == NULL)) {
/* only have 1 pending for blocked non-rt signals */
/* to avoid accumulating signals if we're slow in presence of
* a high-rate itimer we only keep 2 alarm signals (PR 596768)
*/
if (sig_is_alarm_signal(sig)) {
if (info->sigpending[sig] != NULL &&
info->sigpending[sig]->next != NULL) {
ASSERT(info->sigpending[sig]->next->next == NULL);
/* keep the oldest, replace newer w/ brand-new one, for
* more spread-out alarms
*/
sigpending_t *temp = info->sigpending[sig];
info->sigpending[sig] = temp->next;
special_heap_free(info->sigheap, temp);
info->num_pending--;
LOG(THREAD, LOG_ASYNCH, 2, "3rd pending alarm %d => dropping 2nd\n",
sig);
STATS_INC(num_signals_dropped);
SYSLOG_INTERNAL_WARNING_ONCE("dropping 3rd pending alarm signal");
}
}
/* special heap alloc always uses sizeof(sigpending_t) blocks */
pend = special_heap_alloc(info->sigheap);
ASSERT(sig > 0 && sig <= MAX_SIGNUM);
info->num_pending++;
if (info->num_pending > DYNAMO_OPTION(max_pending_signals) &&
!info->multiple_pending_units)
info->multiple_pending_units = true;
if (info->num_pending >= DYNAMO_OPTION(max_pending_signals)) {
/* We're at the limit of our special heap: one more and it will try to
* allocate a new unit, which is unsafe as it acquires locks. We take
* several steps: we notify the user; we check for this on delivery as
* well and proactively allocate a new unit in a safer context.
* XXX: Perhaps we should drop some signals here?
*/
DO_ONCE({
char max_string[32];
snprintf(max_string, BUFFER_SIZE_ELEMENTS(max_string), "%d",
DYNAMO_OPTION(max_pending_signals));
NULL_TERMINATE_BUFFER(max_string);
SYSLOG(SYSLOG_WARNING, MAX_PENDING_SIGNALS, 3, get_application_name(),
get_application_pid(), max_string);
});
}
pend->next = info->sigpending[sig];
info->sigpending[sig] = pend;
pend->unblocked = !blocked;
/* FIXME: note that for asynchronous signals we don't need to
* bother to record exact machine context, even entire frame,
* since don't want to pass dynamo pc context to app handler.
* only copy frame for synchronous signals? those only
* happen while in cache? but for asynch, we would have to
* construct our own frame...kind of a pain.
*/
copy_frame_to_pending(dcontext, sig, frame _IF_CLIENT(access_address));
/* i#1145: check whether we should auto-restart an interrupted syscall */
if (at_auto_restart_syscall) {
/* Adjust the pending frame to restart the syscall, if applicable */
sigframe_rt_t *frame = &(info->sigpending[sig]->rt_frame);
sigcontext_t *sc_pend = get_sigcontext_from_rt_frame(frame);
if (adjust_syscall_for_restart(dcontext, info, sig, sc_pend, f,
orig_retval_reg)) {
/* We're going to re-start this syscall after we go
* back to d_r_dispatch, run the post-syscall handler (for -EINTR),
* and deliver the signal. We've adjusted the sigcontext
* for re-start on the sigreturn, but we need to tell
* execute_handler_from_dispatch() to use our sigcontext
* and not the mcontext.
* A client will see a second set of pre + post handlers for
* the restart, which seems reasonable, given the signal in
* between.
*/
info->sigpending[sig]->use_sigcontext = true;
}
}
} else {
/* For clients, we document that we do not pass to them
* unless we're prepared to deliver to app. We would have
* to change our model to pass them non-final-translated
* contexts for delayable signals in order to give them
* signals as soon as they come in. Xref i#182/PR 449996.
*/
LOG(THREAD, LOG_ASYNCH, 3,
"\tnon-rt signal already in queue, ignoring this one!\n");
}
if (!blocked && !dcontext->signals_pending)
dcontext->signals_pending = 1;
}
ostd->processing_signal--;
}
/* Distinguish SYS_kill-generated from instruction-generated signals.
* If sent from another process we can't tell, but if sent from this
* thread the interruption point should be our own post-syscall.
* FIXME PR 368277: for other threads in same process we should set a flag
* and identify them as well.
* FIXME: for faults like SIGILL we could examine the interrupted pc
* to see whether it is capable of generating such a fault (see code
* used in handle_nudge_signal()).
*/
static bool
is_sys_kill(dcontext_t *dcontext, byte *pc, byte *xsp, kernel_siginfo_t *info)
{
#if !defined(VMX86_SERVER) && !defined(MACOS) /* does not use SI_KERNEL */
/* i#133: use si_code to distinguish user-sent signals.
* Even 2.2 Linux kernel supports <=0 meaning user-sent (except
* SIGIO) so we assume we can rely on it.
*/
if (info->si_code <= 0)
return true;
#endif
return (is_at_do_syscall(dcontext, pc, xsp) &&
(dcontext->sys_num == SYS_kill ||
#ifdef LINUX
dcontext->sys_num == SYS_tkill || dcontext->sys_num == SYS_tgkill ||
dcontext->sys_num == SYS_rt_sigqueueinfo
#elif defined(MACOS)
dcontext->sys_num == SYS___pthread_kill
#endif
));
}
static byte *
compute_memory_target(dcontext_t *dcontext, cache_pc instr_cache_pc,
kernel_ucontext_t *uc, kernel_siginfo_t *si, bool *write)
{
sigcontext_t *sc = SIGCXT_FROM_UCXT(uc);
byte *target = NULL;
instr_t instr;
priv_mcontext_t mc;
uint memopidx, memoppos, memopsize;
opnd_t memop;
bool found_target = false;
bool in_maps;
bool use_allmem = false;
uint prot;
IF_ARM(dr_isa_mode_t old_mode;)
LOG(THREAD, LOG_ALL, 2,
"computing memory target for " PFX " causing SIGSEGV, kernel claims it is " PFX
"\n",
instr_cache_pc, (byte *)si->si_addr);
/* ARM's sigcontext_t has a "fault_address" field but it also seems unreliable */
IF_ARM(LOG(THREAD, LOG_ALL, 2, "fault_address: " PFX "\n", sc->fault_address));
/* We used to do a memory query to check if instr_cache_pc is readable, but
* now we use TRY/EXCEPT because we don't have the instr length and the OS
* query is expensive. If decoding faults, the signal handler will longjmp
* out before it calls us recursively.
*/
instr_init(dcontext, &instr);
IF_ARM({
/* Be sure to use the interrupted mode and not the last-dispatch mode */
dr_set_isa_mode(dcontext, get_pc_mode_from_cpsr(sc), &old_mode);
});
TRY_EXCEPT(dcontext, { decode(dcontext, instr_cache_pc, &instr); },
{
return NULL; /* instr_cache_pc was unreadable */
});
IF_ARM(dr_set_isa_mode(dcontext, old_mode, NULL));
if (!instr_valid(&instr)) {
LOG(THREAD, LOG_ALL, 2,
"WARNING: got SIGSEGV for invalid instr at cache pc " PFX "\n",
instr_cache_pc);
ASSERT_NOT_REACHED();
instr_free(dcontext, &instr);
return NULL;
}
ucontext_to_mcontext(&mc, uc);
ASSERT(write != NULL);
/* i#1009: If si_addr is plausibly one of the memory operands of the
* faulting instruction, assume the target was si_addr. If none of the
* memops match, fall back to checking page protections, which can be racy.
* For si_addr == NULL, we fall back to the protection check because it's
* too likely to be a valid memop and we can live with a race on a page that
* is typically unmapped.
*/
if (si->si_code == SEGV_ACCERR && si->si_addr != NULL) {
for (memopidx = 0; instr_compute_address_ex_priv(&instr, &mc, memopidx, &target,
write, &memoppos);
memopidx++) {
/* i#1045: check whether operand and si_addr overlap */
memop = *write ? instr_get_dst(&instr, memoppos)
: instr_get_src(&instr, memoppos);
memopsize = opnd_size_in_bytes(opnd_get_size(memop));
LOG(THREAD, LOG_ALL, 2, "memory operand %u has address " PFX " and size %u\n",
memopidx, target, memopsize);
if ((byte *)si->si_addr >= target &&
(byte *)si->si_addr < target + memopsize) {
target = (byte *)si->si_addr;
found_target = true;
break;
}
}
}
/* For fcache faults, use all_memory_areas, which is faster but acquires
* locks. If it's possible we're in DR, go to the OS to avoid deadlock.
*/
if (DYNAMO_OPTION(use_all_memory_areas)) {
use_allmem = safe_is_in_fcache(dcontext, instr_cache_pc, (byte *)sc->SC_XSP);
}
if (!found_target) {
if (si->si_addr != NULL) {
LOG(THREAD, LOG_ALL, 3, "%s: falling back to racy protection checks\n",
__FUNCTION__);
}
/* i#115/PR 394984: consider all memops */
for (memopidx = 0;
instr_compute_address_ex_priv(&instr, &mc, memopidx, &target, write, NULL);
memopidx++) {
if (use_allmem) {
in_maps = get_memory_info(target, NULL, NULL, &prot);
} else {
in_maps = get_memory_info_from_os(target, NULL, NULL, &prot);
}
if ((!in_maps || !TEST(MEMPROT_READ, prot)) ||
(*write && !TEST(MEMPROT_WRITE, prot))) {
found_target = true;
break;
}
}
}
if (!found_target) {
/* probably an NX fault: how tell whether kernel enforcing? */
in_maps = get_memory_info_from_os(instr_cache_pc, NULL, NULL, &prot);
if (!in_maps || !TEST(MEMPROT_EXEC, prot)) {
target = instr_cache_pc;
found_target = true;
}
}
/* we may still not find target, e.g. for SYS_kill(SIGSEGV) */
if (!found_target)
target = NULL;
DOLOG(2, LOG_ALL, {
LOG(THREAD, LOG_ALL, 2,
"For SIGSEGV at cache pc " PFX ", computed target %s " PFX "\n",
instr_cache_pc, *write ? "write" : "read", target);
d_r_loginst(dcontext, 2, &instr, "\tfaulting instr");
});
instr_free(dcontext, &instr);
return target;
}
/* If native_state is true, assumes the fault is not in the cache and thus
* does not need translation but rather should always be re-executed.
*/
static bool
check_for_modified_code(dcontext_t *dcontext, cache_pc instr_cache_pc,
kernel_ucontext_t *uc, byte *target, bool native_state)
{
/* special case: we expect a seg fault for executable regions
* that were writable and marked read-only by us.
* have to figure out the target address!
* unfortunately the OS doesn't tell us, nor whether it's a write.
* FIXME: if sent from SYS_kill(SIGSEGV), the pc will be post-syscall,
* and if that post-syscall instr is a write that could have faulted,
* how can we tell the difference?
*/
if (was_executable_area_writable(target)) {
/* translate instr_cache_pc to original app pc
* DO NOT use translate_sigcontext, don't want to change the
* signal frame or else we'll lose control when we try to
* return to signal pc!
*/
app_pc next_pc, translated_pc = NULL;
fragment_t *f = NULL;
fragment_t wrapper;
ASSERT((cache_pc)SIGCXT_FROM_UCXT(uc)->SC_XIP == instr_cache_pc);
if (!native_state) {
/* For safe recreation we need to either be couldbelinking or hold
* the initexit lock (to keep someone from flushing current
* fragment), the initexit lock is easier
*/
d_r_mutex_lock(&thread_initexit_lock);
/* cache the fragment since pclookup is expensive for coarse units (i#658) */
f = fragment_pclookup(dcontext, instr_cache_pc, &wrapper);
translated_pc = recreate_app_pc(dcontext, instr_cache_pc, f);
ASSERT(translated_pc != NULL);
d_r_mutex_unlock(&thread_initexit_lock);
}
next_pc =
handle_modified_code(dcontext, instr_cache_pc, translated_pc, target, f);
if (!native_state) {
/* going to exit from middle of fragment (at the write) so will mess up
* trace building
*/
if (is_building_trace(dcontext)) {
LOG(THREAD, LOG_ASYNCH, 3, "\tsquashing trace-in-progress\n");
trace_abort(dcontext);
}
}
if (next_pc == NULL) {
/* re-execute the write -- just have master_signal_handler return */
return true;
} else {
ASSERT(!native_state);
/* Do not resume execution in cache, go back to d_r_dispatch. */
transfer_from_sig_handler_to_fcache_return(
dcontext, uc, NULL, SIGSEGV, next_pc,
(linkstub_t *)get_selfmod_linkstub(), false);
/* now have master_signal_handler return */
return true;
}
}
return false;
}
#ifndef HAVE_SIGALTSTACK
/* The exact layout of this struct is relied on in master_signal_handler()
* in x86.asm.
*/
struct clone_and_swap_args {
byte *stack;
byte *tos;
};
/* Helper function for swapping handler to dstack */
bool
sig_should_swap_stack(struct clone_and_swap_args *args, kernel_ucontext_t *ucxt)
{
byte *cur_esp;
dcontext_t *dcontext = get_thread_private_dcontext();
if (dcontext == NULL)
return false;
GET_STACK_PTR(cur_esp);
if (!is_on_dstack(dcontext, cur_esp)) {
sigcontext_t *sc = SIGCXT_FROM_UCXT(ucxt);
/* Pass back the proper args to clone_and_swap_stack: we want to
* copy to dstack from the tos at the signal interruption point.
*/
args->stack = dcontext->dstack;
/* leave room for fpstate */
args->stack -= signal_frame_extra_size(true);
args->stack = (byte *)ALIGN_BACKWARD(args->stack, XSTATE_ALIGNMENT);
args->tos = (byte *)sc->SC_XSP;
return true;
} else
return false;
}
#endif
/* Helper that takes over the current thread signaled via SUSPEND_SIGNAL. Kept
* separate mostly to keep the priv_mcontext_t allocation out of
* master_signal_handler_C.
* If it returns, it returns false, and the signal should be squashed.
*/
static bool
sig_take_over(kernel_ucontext_t *uc)
{
priv_mcontext_t mc;
ucontext_to_mcontext(&mc, uc);
/* We don't want our own blocked signals: we want the app's, stored in the frame. */
if (!os_thread_take_over(&mc, SIGMASK_FROM_UCXT(uc)))
return false;
ASSERT_NOT_REACHED(); /* shouldn't return */
return true; /* make compiler happy */
}
static bool
is_safe_read_ucxt(kernel_ucontext_t *ucxt)
{
app_pc pc = (app_pc)SIGCXT_FROM_UCXT(ucxt)->SC_XIP;
return is_safe_read_pc(pc);
}
/* the master signal handler
* WARNING: behavior varies with different versions of the kernel!
* sigaction support was only added with 2.2
*/
#ifndef X86_32
/* stub in x86.asm passes our xsp to us */
# ifdef MACOS
void
master_signal_handler_C(handler_t handler, int style, int sig, kernel_siginfo_t *siginfo,
kernel_ucontext_t *ucxt, byte *xsp)
# else
void
master_signal_handler_C(int sig, kernel_siginfo_t *siginfo, kernel_ucontext_t *ucxt,
byte *xsp)
# endif
#else
/* On ia32, adding a parameter disturbs the frame we're trying to capture, so we
* add an intermediate frame and read the normal params off the stack directly.
*/
void
master_signal_handler_C(byte *xsp)
#endif
{
#ifdef MACOS64
/* The kernel aligns to 16 after setting up the frame, so we instead compute
* from the siginfo pointer.
* XXX: 32-bit does the same thing: how was it working?!?
*/
sigframe_rt_t *frame = (sigframe_rt_t *)((byte *)siginfo - sizeof(frame->mc));
/* The kernel's method of aligning overshoots. */
# define KERNEL_ALIGN_BACK(val, align) (((val)-align) & -(align))
/* If this assert fails, we may be seeing an AVX512 frame. */
ASSERT(KERNEL_ALIGN_BACK((ptr_uint_t)frame, 16) - 8 == (ptr_uint_t)xsp &&
"AVX512 frames not yet supported");
#else
sigframe_rt_t *frame = (sigframe_rt_t *)xsp;
#endif
#ifdef X86_32
/* Read the normal arguments from the frame. */
int sig = frame->sig;
kernel_siginfo_t *siginfo = frame->pinfo;
kernel_ucontext_t *ucxt = frame->puc;
#endif /* !X64 */
sigcontext_t *sc = SIGCXT_FROM_UCXT(ucxt);
thread_record_t *tr;
#ifdef DEBUG
uint level = 2;
# if !defined(HAVE_MEMINFO)
/* avoid logging every single TRY probe fault */
if (!dynamo_initialized)
level = 5;
# endif
#endif
bool local;
#if defined(MACOS) && !defined(X64)
/* The kernel clears fs, so we have to re-instate our selector, if
* it was set in the first place.
*/
if (sc->__ss.__fs != 0)
tls_reinstate_selector(sc->__ss.__fs);
#endif
#ifdef X86
/* i#2089: For is_thread_tls_initialized() we need a safe_read path that does not
* do any logging or call get_thread_private_dcontext() as those will recurse.
* This path is global so there's no SELF_PROTECT_LOCAL and we also bypass
* the ENTERING_DR() for this short path.
*/
if (sig == SIGSEGV && sc->SC_XIP == (ptr_uint_t)safe_read_tls_magic) {
sc->SC_RETURN_REG = 0;
sc->SC_XIP = (reg_t)safe_read_tls_magic_recover;
return;
} else if (sig == SIGSEGV && sc->SC_XIP == (ptr_uint_t)safe_read_tls_self) {
sc->SC_RETURN_REG = 0;
sc->SC_XIP = (reg_t)safe_read_tls_self_recover;
return;
} else if (sig == SIGSEGV && sc->SC_XIP == (ptr_uint_t)safe_read_tls_app_self) {
sc->SC_RETURN_REG = 0;
sc->SC_XIP = (reg_t)safe_read_tls_app_self_recover;
return;
}
#endif
dcontext_t *dcontext = get_thread_private_dcontext();
#ifdef MACOS
# ifdef X64
ASSERT((YMM_ENABLED() && ucxt->uc_mcsize == sizeof(_STRUCT_MCONTEXT_AVX64)) ||
(!YMM_ENABLED() && ucxt->uc_mcsize == sizeof(_STRUCT_MCONTEXT64)));
# else
ASSERT((YMM_ENABLED() && ucxt->uc_mcsize == sizeof(_STRUCT_MCONTEXT_AVX32)) ||
(!YMM_ENABLED() && ucxt->uc_mcsize == sizeof(_STRUCT_MCONTEXT)));
# endif
#endif
/* i#350: To support safe_read or TRY_EXCEPT without a dcontext, use the
* global dcontext
* when handling safe_read faults. This lets us pass the check for a
* dcontext below and causes us to use the global log.
*/
if (dcontext == NULL && (sig == SIGSEGV || sig == SIGBUS) &&
(is_safe_read_ucxt(ucxt) ||
(!dynamo_initialized && global_try_except.try_except_state != NULL))) {
dcontext = GLOBAL_DCONTEXT;
}
if (dynamo_exited && d_r_get_num_threads() > 1 && sig == SIGSEGV) {
/* PR 470957: this is almost certainly a race so just squelch it.
* We live w/ the risk that it was holding a lock our release-build
* exit code needs.
*/
exit_thread_syscall(1);
}
/* FIXME: ensure the path for recording a pending signal does not grab any DR locks
* that could have been interrupted
* e.g., synchronize_dynamic_options grabs the stats_lock!
*/
if (sig == SUSPEND_SIGNAL) {
if (proc_get_vendor() == VENDOR_AMD) {
/* i#3356: Work around an AMD processor bug where it does not clear the
* hidden gs base when the gs selector is written. Pre-4.7 Linux kernels
* leave the prior thread's base in place on a switch due to this.
* We can thus come here and get the wrong dcontext on attach; worse,
* we can get NULL here but the wrong one later during init. It's
* safest to just set a non-zero value (the kernel ignores zero) for all
* unknown threads here. There are no problems for non-attach takeover.
*/
if (dcontext == NULL || dcontext->owning_thread != get_sys_thread_id()) {
/* tls_thread_preinit() further rules out a temp-native dcontext
* and avoids clobbering it, to preserve the thread_lookup() case
* below (which we do not want to run first as we could swap to
* the incorrect dcontext midway through it).
*/
if (!tls_thread_preinit()) {
SYSLOG_INTERNAL_ERROR_ONCE("ERROR: Failed to work around AMD context "
"switch bug #3356: crashes or "
"hangs may ensue...");
}
dcontext = NULL;
}
}
}
if (dcontext == NULL &&
/* Check for a temporarily-native thread we're synch-ing with. */
(sig == SUSPEND_SIGNAL
#ifdef X86
||
(INTERNAL_OPTION(safe_read_tls_init) &&
/* Check for whether this is a thread with its invalid sentinel magic set.
* In this case, we assume that it is either a thread that is currently
* temporarily-native via API like DR_EMIT_GO_NATIVE, or a thread in the
* clone window. We know by inspection of our own code that it is safe to
* call thread_lookup for either case thread makes a clone or was just
* cloned. i.e. thread_lookup requires a lock that must not be held by the
* calling thread (i#2921).
* XXX: what is ARM doing, any special case w/ dcontext == NULL?
*/
/* Don't do this if we're detaching as we risk a safe-read fault after
* we've removed our handler (i#3535).
*/
detacher_tid == INVALID_THREAD_ID && safe_read_tls_magic() == TLS_MAGIC_INVALID)
#endif
)) {
tr = thread_lookup(get_sys_thread_id());
if (tr != NULL)
dcontext = tr->dcontext;
}
if (dcontext == NULL ||
(dcontext != GLOBAL_DCONTEXT &&
(dcontext->signal_field == NULL ||
!((thread_sig_info_t *)dcontext->signal_field)->fully_initialized))) {
/* XXX i#26: this could be a signal arbitrarily sent to this thread.
* We could try to route it to another thread, using a global queue
* of pending signals. But what if it was targeted to this thread
* via SYS_{tgkill,tkill}? Can we tell the difference, even if
* we watch the kill syscalls: could come from another process?
* For now we use our native thread signal delivery mechanism.
*/
if (sig_is_alarm_signal(sig)) {
/* Assuming an alarm during thread exit or init (xref PR 596127,
* i#359). Delivering it sometimes works, but we could be mid-init
* where it's not easy and we could crash b/c of in-between state
* (observed for alarm mid-signal_thread_init where info->app_sigaction
* is still NULL).
*/
return;
} else if (sig == SUSPEND_SIGNAL && dcontext == NULL) {
/* We sent SUSPEND_SIGNAL to a thread we don't control (no
* dcontext), which means we want to take over.
*/
ASSERT(!doing_detach);
if (!sig_take_over(ucxt))
return;
ASSERT_NOT_REACHED(); /* else, shouldn't return */
} else {
LOG(GLOBAL, LOG_ASYNCH, 1,
"signal with no siginfo (tid=%d, sig=%d): attempting to deliver to "
"native thread\n",
get_sys_thread_id(), sig);
DOLOG(1, LOG_ASYNCH, { dump_sigcontext(GLOBAL_DCONTEXT, sc); });
}
/* For can_always_delay[sig] we could just return and drop it, but we
* try to perturb the app behavior less with a native signal frame:
*/
execute_native_handler(dcontext, sig, frame);
return;
}
/* we may be entering dynamo from code cache! */
/* Note that this is unsafe if -single_thread_in_DR => we grab a lock =>
* hang if signal interrupts DR: but we don't really support that option
*/
ENTERING_DR();
if (dcontext == GLOBAL_DCONTEXT) {
local = false;
tr = thread_lookup(get_sys_thread_id());
} else {
tr = dcontext->thread_record;
local = local_heap_protected(dcontext);
if (local)
SELF_PROTECT_LOCAL(dcontext, WRITABLE);
}
/* i#1921: For proper native execution with re-takeover we need to propagate
* signals to app handlers while native. For now we do not support re-takeover
* and we give up our handlers via signal_remove_handlers().
*/
ASSERT(tr == NULL || tr->under_dynamo_control || IS_CLIENT_THREAD(dcontext) ||
sig == SUSPEND_SIGNAL);
LOG(THREAD, LOG_ASYNCH, level,
"\nmaster_signal_handler: thread=%d, sig=%d, xsp=" PFX ", retaddr=" PFX "\n",
get_sys_thread_id(), sig, xsp, *((byte **)xsp));
LOG(THREAD, LOG_ASYNCH, level + 1,
"siginfo: sig = %d, pid = %d, status = %d, errno = %d, si_code = %d\n",
siginfo->si_signo, siginfo->si_pid, siginfo->si_status, siginfo->si_errno,
siginfo->si_code);
DOLOG(level + 1, LOG_ASYNCH, { dump_sigcontext(dcontext, sc); });
#if defined(X86_32) && !defined(VMX86_SERVER) && defined(LINUX)
/* FIXME case 6700: 2.6.9 (FC3) kernel sets up our frame with a pretcode
* of 0x440. This happens if our restorer is unspecified (though 2.6.9
* src code shows setting the restorer to a default value in that case...)
* or if we explicitly point at dynamorio_sigreturn. I couldn't figure
* out why it kept putting 0x440 there. So we fix the issue w/ this
* hardcoded return.
* This hack causes vmkernel to kill the process on sigreturn due to
* vmkernel's non-standard sigreturn semantics. PR 404712.
*/
*((byte **)xsp) = (byte *)dynamorio_sigreturn;
#endif
/* N.B.:
* ucontext_t is defined in two different places. The one we get
* included is /usr/include/sys/ucontext.h, which would have us
* doing this:
* void *pc = (void *) ucxt->uc_mcontext.gregs[EIP];
* However, EIP is not defined for us (used to be in older
* RedHat version) unless we define __USE_GNU, which we don't want to do
* for other reasons, so we'd have to also say:
* #define EIP 14
* Instead we go by the ucontext_t definition in
* /usr/include/asm/ucontext.h, which has it containing a sigcontext struct,
* defined in /usr/include/asm/sigcontext.h. This is the definition used
* by the kernel. The two definitions are field-for-field
* identical except that the sys one has an fpstate struct at the end --
* but the next field in the frame is an fpstate. The only mystery
* is why the rt frame is declared as ucontext instead of sigcontext.
* The kernel's version of ucontext must be the asm one!
* And the sys one grabs the next field of the frame.
* Also note that mcontext_t.fpregs == sigcontext.fpstate is NULL if
* floating point operations have not been used (lazy fp state saving).
* Also, sigset_t has different sizes according to kernel (8 bytes) vs.
* glibc (128 bytes?).
*/
switch (sig) {
case SIGBUS: /* PR 313665: look for DR crashes on unaligned memory or mmap bounds */
case SIGSEGV: {
/* Older kernels do NOT fill out the signal-specific fields of siginfo,
* except for SIGCHLD. Thus we cannot do this:
* void *pc = (void*) siginfo->si_addr;
* Thus we must use the third argument, which is a ucontext_t (see above)
*/
void *pc = (void *)sc->SC_XIP;
bool syscall_signal = false; /* signal came from syscall? */
bool is_write = false;
byte *target;
bool is_DR_exception = false;
#ifdef SIDELINE
if (dcontext == NULL) {
SYSLOG_INTERNAL_ERROR("seg fault in sideline thread -- NULL dcontext!");
ASSERT_NOT_REACHED();
}
#endif
if (is_safe_read_ucxt(ucxt) ||
(!dynamo_initialized && global_try_except.try_except_state != NULL) ||
dcontext->try_except.try_except_state != NULL) {
/* handle our own TRY/EXCEPT */
try_except_context_t *try_cxt;
#ifdef HAVE_MEMINFO
/* our probe produces many of these every run */
/* since we use for safe_*, making a _ONCE */
SYSLOG_INTERNAL_WARNING_ONCE("(1+x) Handling our fault in a TRY at " PFX, pc);
#endif
LOG(THREAD, LOG_ALL, level, "TRY fault at " PFX "\n", pc);
if (TEST(DUMPCORE_TRY_EXCEPT, DYNAMO_OPTION(dumpcore_mask)))
os_dump_core("try/except fault");
if (is_safe_read_ucxt(ucxt)) {
sc->SC_XIP = (reg_t)safe_read_resume_pc();
/* Break out to log the normal return from the signal handler.
*/
break;
}
try_cxt = (dcontext != NULL) ? dcontext->try_except.try_except_state
: global_try_except.try_except_state;
ASSERT(try_cxt != NULL);
/* The exception interception code did an ENTER so we must EXIT here */
EXITING_DR();
/* Since we have no sigreturn we have to restore the mask
* manually, just like siglongjmp(). i#226/PR 492568: we rely
* on the kernel storing the prior mask in ucxt, so we do not
* need to store it on every setjmp.
*/
/* Verify that there's no scenario where the mask gets changed prior
* to a fault inside a try. This relies on dr_setjmp_sigmask() filling
* in the mask, which we only bother to do in debug build.
*/
ASSERT(memcmp(&try_cxt->context.sigmask, &ucxt->uc_sigmask,
sizeof(ucxt->uc_sigmask)) == 0);
sigprocmask_syscall(SIG_SETMASK, SIGMASK_FROM_UCXT(ucxt), NULL,
sizeof(ucxt->uc_sigmask));
DR_LONGJMP(&try_cxt->context, LONGJMP_EXCEPTION);
ASSERT_NOT_REACHED();
}
target = compute_memory_target(dcontext, pc, ucxt, siginfo, &is_write);
#ifdef CLIENT_INTERFACE
if (CLIENTS_EXIST() && is_in_client_lib(pc)) {
/* i#1354: client might write to a page we made read-only.
* If so, handle the fault and re-execute it, if it's safe to do so
* (we document these criteria under DR_MEMPROT_PRETEND_WRITE).
*/
if (is_write && !is_couldbelinking(dcontext) && OWN_NO_LOCKS(dcontext) &&
check_for_modified_code(dcontext, pc, ucxt, target, true /*native*/))
break;
abort_on_fault(dcontext, DUMPCORE_CLIENT_EXCEPTION, pc, target, sig, frame,
exception_label_client, (sig == SIGSEGV) ? "SEGV" : "BUS",
" client library");
ASSERT_NOT_REACHED();
}
#endif
/* For !HAVE_MEMINFO, we cannot compute the target until
* after the try/except check b/c compute_memory_target()
* calls get_memory_info_from_os() which does a probe: and the
* try/except could be from a probe itself. A try/except that
* triggers a stack overflow should recover on the longjmp, so
* this order should be fine.
*/
/* FIXME: share code with Windows callback.c */
/* FIXME PR 205795: in_fcache and is_dynamo_address do grab locks! */
if ((is_on_dstack(dcontext, (byte *)sc->SC_XSP)
/* PR 302951: clean call arg processing => pass to app/client.
* Rather than call the risky in_fcache we check whereami. */
IF_CLIENT_INTERFACE(&&(dcontext->whereami != DR_WHERE_FCACHE))) ||
is_on_alt_stack(dcontext, (byte *)sc->SC_XSP) ||
is_on_initstack((byte *)sc->SC_XSP)) {
/* Checks here need to cover everything that record_pending_signal()
* thinks is non-fcache, non-gencode: else that routine will kill
* process since can't delay or re-execute (i#195/PR 453964).
*/
is_DR_exception = true;
} else if (!safe_is_in_fcache(dcontext, pc, (byte *)sc->SC_XSP) &&
(in_generated_routine(dcontext, pc) ||
is_at_do_syscall(dcontext, pc, (byte *)sc->SC_XSP) ||
is_dynamo_address(pc))) {
#ifdef CLIENT_INTERFACE
if (!in_generated_routine(dcontext, pc) &&
!is_at_do_syscall(dcontext, pc, (byte *)sc->SC_XSP)) {
/* PR 451074: client needs a chance to handle exceptions in its
* own gencode. client_exception_event() won't return if client
* wants to re-execute faulting instr.
*/
sigcontext_t sc_interrupted = *get_sigcontext_from_rt_frame(frame);
dr_signal_action_t action = send_signal_to_client(
dcontext, sig, frame, sc, target, false /*!blocked*/, NULL);
if (action != DR_SIGNAL_DELIVER && /* for delivery, continue below */
!handle_client_action_from_cache(dcontext, sig, action, frame, sc,
&sc_interrupted,
false /*!blocked*/)) {
/* client handled fault */
break;
}
}
#endif
is_DR_exception = true;
}
if (is_DR_exception) {
/* kill(getpid(), SIGSEGV) looks just like a SIGSEGV in the store of eax
* to mcontext after the syscall instr in do_syscall -- try to distinguish:
*/
if (is_sys_kill(dcontext, pc, (byte *)sc->SC_XSP, siginfo)) {
LOG(THREAD, LOG_ALL, 2,
"assuming SIGSEGV at post-do-syscall is kill, not our write fault\n");
syscall_signal = true;
}
if (!syscall_signal) {
if (check_in_last_thread_vm_area(dcontext, target)) {
/* See comments in callback.c as well.
* FIXME: try to share code
*/
SYSLOG_INTERNAL_WARNING("(decode) exception in last area, "
"DR pc=" PFX ", app pc=" PFX,
pc, target);
STATS_INC(num_exceptions_decode);
if (is_building_trace(dcontext)) {
LOG(THREAD, LOG_ASYNCH, 2,
"intercept_exception: "
"squashing old trace\n");
trace_abort(dcontext);
}
/* we do get faults when not building a bb: e.g.,
* ret_after_call_check does decoding (case 9396) */
if (dcontext->bb_build_info != NULL) {
/* must have been building a bb at the time */
bb_build_abort(dcontext, true /*clean vm area*/, true /*unlock*/);
}
/* Since we have no sigreturn we have to restore the mask manually */
unblock_all_signals(NULL);
/* Let's pass it back to the application - memory is unreadable */
if (TEST(DUMPCORE_FORGE_UNREAD_EXEC, DYNAMO_OPTION(dumpcore_mask)))
os_dump_core("Warning: Racy app execution (decode unreadable)");
os_forge_exception(target, UNREADABLE_MEMORY_EXECUTION_EXCEPTION);
ASSERT_NOT_REACHED();
} else {
abort_on_DR_fault(dcontext, pc, target, sig, frame,
(sig == SIGSEGV) ? "SEGV" : "BUS",
in_generated_routine(dcontext, pc) ? " generated"
: "");
}
}
}
/* if get here, pass the signal to the app */
ASSERT(pc != 0); /* shouldn't get here */
if (sig == SIGSEGV && !syscall_signal /*only for in-cache signals*/) {
/* special case: we expect a seg fault for executable regions
* that were writable and marked read-only by us.
*/
if (is_write &&
check_for_modified_code(dcontext, pc, ucxt, target, false /*!native*/)) {
/* it was our signal, so don't pass to app -- return now */
break;
}
}
/* pass it to the application (or client) */
if (dcontext->currently_stopped) {
execute_native_handler(dcontext, sig, frame);
break;
}
LOG(THREAD, LOG_ALL, 1,
"** Received SIG%s at cache pc " PFX " in thread " TIDFMT "\n",
(sig == SIGSEGV) ? "SEGV" : "BUS", pc, d_r_get_thread_id());
ASSERT(syscall_signal || safe_is_in_fcache(dcontext, pc, (byte *)sc->SC_XSP));
/* we do not call trace_abort() here since we may need to
* translate from a temp private bb (i#376): but all paths
* that deliver the signal or redirect will call it
*/
record_pending_signal(dcontext, sig, ucxt, frame, false _IF_CLIENT(target));
break;
}
/* PR 212090: the signal we use to suspend threads */
case SUSPEND_SIGNAL:
if (handle_suspend_signal(dcontext, ucxt, frame)) {
/* i#1921: see comment above */
ASSERT(tr == NULL || tr->under_dynamo_control || IS_CLIENT_THREAD(dcontext));
record_pending_signal(dcontext, sig, ucxt, frame, false _IF_CLIENT(NULL));
}
/* else, don't deliver to app */
break;
/* i#61/PR 211530: the signal we use for nudges */
case NUDGESIG_SIGNUM:
if (handle_nudge_signal(dcontext, siginfo, ucxt))
record_pending_signal(dcontext, sig, ucxt, frame, false _IF_CLIENT(NULL));
/* else, don't deliver to app */
break;
case SIGALRM:
case SIGVTALRM:
case SIGPROF:
if (handle_alarm(dcontext, sig, ucxt))
record_pending_signal(dcontext, sig, ucxt, frame, false _IF_CLIENT(NULL));
/* else, don't deliver to app */
break;
#ifdef SIDELINE
case SIGCHLD: {
int status = siginfo->si_status;
if (siginfo->si_pid == 0) {
/* FIXME: with older versions of linux the sigchld fields of
* siginfo are not filled in properly!
* This is my attempt to handle that, pid seems to be 0
*/
break;
}
if (status != 0) {
LOG(THREAD, LOG_ALL, 0, "*** Child thread died with error %d\n", status);
ASSERT_NOT_REACHED();
}
break;
}
#endif
default: {
record_pending_signal(dcontext, sig, ucxt, frame, false _IF_CLIENT(NULL));
break;
}
} /* end switch */
LOG(THREAD, LOG_ASYNCH, level,
"\tmaster_signal_handler %d returning now to " PFX "\n\n", sig, sc->SC_XIP);
/* Ensure we didn't get the app's sigstack into our frame. On Mac, the kernel
* doesn't use the frame's uc_stack, so we limit this to Linux.
* The pointers may be different if a thread is on its way to exit, and the app's
* sigstack was already restored (i#3369).
*/
IF_LINUX(ASSERT(dcontext == NULL || dcontext == GLOBAL_DCONTEXT || dynamo_exited ||
dcontext->is_exiting ||
frame->uc.uc_stack.ss_sp ==
((thread_sig_info_t *)dcontext->signal_field)->sigstack.ss_sp));
/* restore protections */
if (local)
SELF_PROTECT_LOCAL(dcontext, READONLY);
EXITING_DR();
}
static bool
execute_handler_from_cache(dcontext_t *dcontext, int sig, sigframe_rt_t *our_frame,
sigcontext_t *sc_orig,
fragment_t *f _IF_CLIENT(byte *access_address))
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
/* we want to modify the sc in DR's frame */
kernel_ucontext_t *uc = get_ucontext_from_rt_frame(our_frame);
sigcontext_t *sc = SIGCXT_FROM_UCXT(uc);
kernel_sigset_t blocked;
/* Need to get xsp now before get new dcontext.
* This is the translated xsp, so we avoid PR 306410 (cleancall arg fault
* on dstack => handler run on dstack) that Windows hit.
*/
byte *xsp = get_sigstack_frame_ptr(dcontext, info, sig,
our_frame/* take xsp from (translated)
* interruption point */);
#ifdef CLIENT_INTERFACE
sigcontext_t sc_interrupted = *sc;
dr_signal_action_t action = send_signal_to_client(
dcontext, sig, our_frame, sc_orig, access_address, false /*not blocked*/, f);
if (!handle_client_action_from_cache(dcontext, sig, action, our_frame, sc_orig,
&sc_interrupted, false /*!blocked*/))
return false;
#else
if (info->app_sigaction[sig] == NULL ||
info->app_sigaction[sig]->handler == (handler_t)SIG_DFL) {
LOG(THREAD, LOG_ASYNCH, 3, "\taction is SIG_DFL\n");
if (execute_default_from_cache(dcontext, sig, our_frame, sc_orig, false)) {
/* if we haven't terminated, restore original (untranslated) sc
* on request.
* Note, this also restores SIMD regs, if client translated them!
*/
restore_orig_sigcontext(our_frame, sc_orig);
}
return false;
}
ASSERT(info->app_sigaction[sig] != NULL &&
info->app_sigaction[sig]->handler != (handler_t)SIG_IGN &&
info->app_sigaction[sig]->handler != (handler_t)SIG_DFL);
#endif
LOG(THREAD, LOG_ASYNCH, 2, "execute_handler_from_cache for signal %d\n", sig);
RSTATS_INC(num_signals);
/* now that we know it's not a client-involved fault, dump as app fault */
report_app_problem(dcontext, APPFAULT_FAULT, (byte *)sc->SC_XIP, (byte *)sc->SC_FP,
"\nSignal %d delivered to application handler.\n", sig);
LOG(THREAD, LOG_ASYNCH, 3, "\txsp is " PFX "\n", xsp);
/* copy frame to appropriate stack and convert to non-rt if necessary */
copy_frame_to_stack(dcontext, info, sig, our_frame, (void *)xsp, false /*!pending*/);
LOG(THREAD, LOG_ASYNCH, 3, "\tcopied frame from " PFX " to " PFX "\n", our_frame,
xsp);
sigcontext_t *app_sc = get_sigcontext_from_app_frame(info, sig, (void *)xsp);
/* Because of difficulties determining when/if a signal handler
* returns, we do what the kernel does: abandon all of our current
* state, copy what we might need to the handler frame if we come back,
* and then it's ok if the handler doesn't return.
* If it does, we start interpreting afresh when we see sigreturn().
* This routine assumes anything needed to return has been put in the
* frame (only needed for signals queued up while in dynamo), and goes
* ahead and trashes the current dcontext.
*/
/* if we were building a trace, kill it */
if (is_building_trace(dcontext)) {
LOG(THREAD, LOG_ASYNCH, 3, "\tsquashing trace-in-progress\n");
trace_abort(dcontext);
}
/* add to set of blocked signals those in sigaction mask */
blocked = info->app_sigaction[sig]->mask;
/* SA_NOMASK says whether to block sig itself or not */
if ((info->app_sigaction[sig]->flags & SA_NOMASK) == 0)
kernel_sigaddset(&blocked, sig);
set_blocked(dcontext, &blocked, false /*relative: OR these in*/);
/* Doesn't matter what most app registers are, signal handler doesn't
* expect anything except the frame on the stack. We do need to set xsp,
* only because if app wants special signal stack we need to point xsp
* there. (If no special signal stack, this is a nop.)
*/
sc->SC_XSP = (ptr_uint_t)xsp;
/* Set up args to handler: int sig, kernel_siginfo_t *siginfo,
* kernel_ucontext_t *ucxt.
*/
#ifdef X86_64
sc->SC_XDI = sig;
sc->SC_XSI = (reg_t) & ((sigframe_rt_t *)xsp)->info;
sc->SC_XDX = (reg_t) & ((sigframe_rt_t *)xsp)->uc;
#elif defined(AARCHXX)
sc->SC_R0 = sig;
if (IS_RT_FOR_APP(info, sig)) {
sc->SC_R1 = (reg_t) & ((sigframe_rt_t *)xsp)->info;
sc->SC_R2 = (reg_t) & ((sigframe_rt_t *)xsp)->uc;
}
if (sig_has_restorer(info, sig))
sc->SC_LR = (reg_t)info->app_sigaction[sig]->restorer;
else
sc->SC_LR = (reg_t)dynamorio_sigreturn;
# ifndef AARCH64
/* We're going to our fcache_return gencode which uses DEFAULT_ISA_MODE */
set_pc_mode_in_cpsr(sc, DEFAULT_ISA_MODE);
# endif
#endif
/* Set our sigreturn context (NOT for the app: we already copied the
* translated context to the app stack) to point to fcache_return!
* Then we'll go back through kernel, appear in fcache_return,
* and go through d_r_dispatch & interp, without messing up DR stack.
*/
transfer_from_sig_handler_to_fcache_return(
dcontext, uc, app_sc, sig,
/* Make sure handler is next thing we execute */
(app_pc)SIGACT_PRIMARY_HANDLER(info->app_sigaction[sig]),
(linkstub_t *)get_asynch_linkstub(), true);
if ((info->app_sigaction[sig]->flags & SA_ONESHOT) != 0) {
/* clear handler now -- can't delete memory since sigreturn,
* others may look at sigaction struct, so we just set to default
*/
info->app_sigaction[sig]->handler = (handler_t)SIG_DFL;
}
LOG(THREAD, LOG_ASYNCH, 3, "\tset next_tag to handler " PFX ", xsp to " PFX "\n",
SIGACT_PRIMARY_HANDLER(info->app_sigaction[sig]), xsp);
return true;
}
static bool
execute_handler_from_dispatch(dcontext_t *dcontext, int sig)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
byte *xsp = get_sigstack_frame_ptr(dcontext, info, sig, NULL);
sigframe_rt_t *frame = &(info->sigpending[sig]->rt_frame);
priv_mcontext_t *mcontext = get_mcontext(dcontext);
sigcontext_t *sc;
kernel_ucontext_t *uc;
kernel_sigset_t blocked;
#ifdef CLIENT_INTERFACE
dr_signal_action_t action;
#else
if (info->app_sigaction[sig] == NULL ||
info->app_sigaction[sig]->handler == (handler_t)SIG_DFL) {
LOG(THREAD, LOG_ASYNCH, 3, "\taction is SIG_DFL\n");
execute_default_from_dispatch(dcontext, sig, frame);
return true;
}
ASSERT(info->app_sigaction[sig] != NULL &&
info->app_sigaction[sig]->handler != (handler_t)SIG_IGN &&
info->app_sigaction[sig]->handler != (handler_t)SIG_DFL);
#endif
LOG(THREAD, LOG_ASYNCH, 2, "execute_handler_from_dispatch for signal %d\n", sig);
RSTATS_INC(num_signals);
/* modify the rtframe before copying to stack so we can pass final
* version to client, and propagate its mods
*/
uc = get_ucontext_from_rt_frame(frame);
sc = SIGCXT_FROM_UCXT(uc);
/* Because of difficulties determining when/if a signal handler
* returns, we do what the kernel does: abandon all of our current
* state, copy what we might need to the handler frame if we come back,
* and then it's ok if the handler doesn't return.
* If it does, we start interpreting afresh when we see sigreturn().
*/
#ifdef DEBUG
if (d_r_stats->loglevel >= 3 && (d_r_stats->logmask & LOG_ASYNCH) != 0) {
LOG(THREAD, LOG_ASYNCH, 3, "original sigcontext " PFX ":\n", sc);
dump_sigcontext(dcontext, sc);
}
#endif
if (info->sigpending[sig]->use_sigcontext) {
LOG(THREAD, LOG_ASYNCH, 2,
"%s: using sigcontext, not mcontext (syscall restart)\n", __FUNCTION__);
} else {
/* copy currently-interrupted-context to frame's context, so we can
* abandon the currently-interrupted context.
*/
mcontext_to_ucontext(uc, mcontext);
}
/* Sigreturn needs the target ISA mode to be set in the T bit in cpsr.
* Since we came from d_r_dispatch, the post-signal target's mode is in dcontext.
*/
IF_ARM(set_pc_mode_in_cpsr(sc, dr_get_isa_mode(dcontext)));
/* mcontext does not contain fp or mmx or xmm state, which may have
* changed since the frame was created (while finishing up interrupted
* fragment prior to returning to d_r_dispatch). Since DR does not touch
* this state except for xmm on x64, we go ahead and copy the
* current state into the frame, and then touch up xmm for x64.
*/
/* FIXME: should this be done for all pending as soon as reach
* d_r_dispatch? what if get two asynch inside same frag prior to exiting
* cache? have issues with fpstate, but also prob with next_tag? FIXME
*/
/* FIXME: we should clear fpstate for app handler itself as that's
* how our own handler is executed.
*/
#if defined(LINUX) && defined(X86)
ASSERT(sc->fpstate != NULL); /* not doing i#641 yet */
save_fpstate(dcontext, frame);
#endif /* LINUX && X86 */
#ifdef DEBUG
if (d_r_stats->loglevel >= 3 && (d_r_stats->logmask & LOG_ASYNCH) != 0) {
LOG(THREAD, LOG_ASYNCH, 3, "new sigcontext " PFX ":\n", sc);
dump_sigcontext(dcontext, sc);
LOG(THREAD, LOG_ASYNCH, 3, "\n");
}
IF_AARCHXX(ASSERT(get_sigcxt_stolen_reg(sc) != (reg_t)*get_dr_tls_base_addr()));
#endif
/* FIXME: other state? debug regs?
* if no syscall allowed between master_ (when frame created) and
* receiving, then don't have to worry about debug regs, etc.
* check for syscall when record pending, if it exists, try to
* receive in pre_system_call or something? what if ignorable? FIXME!
*/
if (!info->sigpending[sig]->use_sigcontext) {
/* for the pc we want the app pc not the cache pc */
sc->SC_XIP = (ptr_uint_t)dcontext->next_tag;
LOG(THREAD, LOG_ASYNCH, 3, "\tset frame's eip to " PFX "\n", sc->SC_XIP);
}
#ifdef CLIENT_INTERFACE
sigcontext_t sc_interrupted = *sc;
action = send_signal_to_client(dcontext, sig, frame, NULL,
info->sigpending[sig]->access_address,
false /*not blocked*/, NULL);
/* in order to pass to the client, we come all the way here for signals
* the app has no handler for
*/
if (action == DR_SIGNAL_REDIRECT) {
/* send_signal_to_client copied mcontext into frame's sc */
priv_mcontext_t *mcontext = get_mcontext(dcontext);
ucontext_to_mcontext(mcontext, uc);
if (is_building_trace(dcontext)) {
LOG(THREAD, LOG_ASYNCH, 3, "\tsquashing trace-in-progress\n");
trace_abort(dcontext);
}
sig_full_cxt_t sc_interrupted_full = { &sc_interrupted, NULL /*not provided*/ };
instrument_kernel_xfer(dcontext, DR_XFER_CLIENT_REDIRECT, sc_interrupted_full,
NULL, NULL, mcontext->pc, mcontext->xsp, osc_empty,
mcontext, sig);
/* We're going back to dispatch and won't use the sigcontext.
* canonicalize_pc_target() will set the dcontext ISA mode for us.
*/
dcontext->next_tag = canonicalize_pc_target(dcontext, mcontext->pc);
return true; /* don't try another signal */
} else if (action == DR_SIGNAL_SUPPRESS ||
(info->app_sigaction[sig] != NULL &&
info->app_sigaction[sig]->handler == (handler_t)SIG_IGN)) {
LOG(THREAD, LOG_ASYNCH, 2, "%s: not delivering!\n",
(action == DR_SIGNAL_SUPPRESS) ? "client suppressing signal"
: "app signal handler is SIG_IGN");
return false;
} else if (action == DR_SIGNAL_BYPASS ||
(info->app_sigaction[sig] == NULL ||
info->app_sigaction[sig]->handler == (handler_t)SIG_DFL)) {
LOG(THREAD, LOG_ASYNCH, 2, "%s: executing default action\n",
(action == DR_SIGNAL_BYPASS) ? "client forcing default"
: "app signal handler is SIG_DFL");
if (info->sigpending[sig]->use_sigcontext) {
/* after the default action we want to go to the sigcontext */
dcontext->next_tag = canonicalize_pc_target(dcontext, (app_pc)sc->SC_XIP);
ucontext_to_mcontext(get_mcontext(dcontext), uc);
IF_ARM(dr_set_isa_mode(dcontext, get_pc_mode_from_cpsr(sc), NULL));
}
execute_default_from_dispatch(dcontext, sig, frame);
return true;
}
CLIENT_ASSERT(action == DR_SIGNAL_DELIVER, "invalid signal event return value");
#endif
/* now that we've made all our changes and given the client a
* chance to make changes, copy the frame to the appropriate stack
* location and convert to non-rt if necessary
*/
copy_frame_to_stack(dcontext, info, sig, frame, xsp, true /*pending*/);
/* now point at the app's frame */
sc = get_sigcontext_from_app_frame(info, sig, (void *)xsp);
ASSERT(info->app_sigaction[sig] != NULL);
/* add to set of blocked signals those in sigaction mask */
blocked = info->app_sigaction[sig]->mask;
/* SA_NOMASK says whether to block sig itself or not */
if ((info->app_sigaction[sig]->flags & SA_NOMASK) == 0)
kernel_sigaddset(&blocked, sig);
set_blocked(dcontext, &blocked, false /*relative: OR these in*/);
/* if we were building a trace, kill it */
if (is_building_trace(dcontext)) {
LOG(THREAD, LOG_ASYNCH, 3, "\tsquashing trace-in-progress\n");
trace_abort(dcontext);
}
/* Doesn't matter what most app registers are, signal handler doesn't
* expect anything except the frame on the stack. We do need to set xsp.
*/
mcontext->xsp = (ptr_uint_t)xsp;
/* Set up args to handler: int sig, kernel_siginfo_t *siginfo,
* kernel_ucontext_t *ucxt.
*/
#ifdef MACOS64
mcontext->xdi = (reg_t)info->app_sigaction[sig]->handler;
int infostyle = TEST(SA_SIGINFO, info->app_sigaction[sig]->flags)
? SIGHAND_STYLE_UC_FLAVOR
: SIGHAND_STYLE_UC_TRAD;
mcontext->xsi = infostyle;
mcontext->xdx = sig;
mcontext->xcx = (reg_t) & ((sigframe_rt_t *)xsp)->info;
mcontext->r8 = (reg_t) & ((sigframe_rt_t *)xsp)->uc;
#elif defined(X86_64)
mcontext->xdi = sig;
mcontext->xsi = (reg_t) & ((sigframe_rt_t *)xsp)->info;
mcontext->xdx = (reg_t) & ((sigframe_rt_t *)xsp)->uc;
#elif defined(AARCHXX)
mcontext->r0 = sig;
if (IS_RT_FOR_APP(info, sig)) {
mcontext->r1 = (reg_t) & ((sigframe_rt_t *)xsp)->info;
mcontext->r2 = (reg_t) & ((sigframe_rt_t *)xsp)->uc;
}
if (sig_has_restorer(info, sig))
mcontext->lr = (reg_t)info->app_sigaction[sig]->restorer;
else
mcontext->lr = (reg_t)dynamorio_sigreturn;
#endif
#ifdef X86
/* Clear eflags DF (signal handler should match function entry ABI) */
mcontext->xflags &= ~EFLAGS_DF;
#endif
/* Make sure handler is next thing we execute */
mcontext->pc = (app_pc)SIGACT_PRIMARY_HANDLER(info->app_sigaction[sig]);
if ((info->app_sigaction[sig]->flags & SA_ONESHOT) != 0) {
/* clear handler now -- can't delete memory since sigreturn,
* others may look at sigaction struct, so we just set to default
*/
info->app_sigaction[sig]->handler = (handler_t)SIG_DFL;
}
#ifdef CLIENT_INTERFACE
sig_full_cxt_t sc_full = { sc, NULL /*not provided*/ };
/* i#4041: Provide the actually-interrupted mid-rseq PC to this event. */
ptr_uint_t official_xl8 = sc->SC_XIP;
sc->SC_XIP =
(ptr_uint_t)translate_last_direct_translation(dcontext, (app_pc)official_xl8);
instrument_kernel_xfer(dcontext, DR_XFER_SIGNAL_DELIVERY, sc_full, NULL, NULL,
mcontext->pc, mcontext->xsp, osc_empty, mcontext, sig);
dcontext->next_tag = canonicalize_pc_target(dcontext, mcontext->pc);
sc->SC_XIP = official_xl8;
#endif
LOG(THREAD, LOG_ASYNCH, 3, "\tset xsp to " PFX "\n", xsp);
return true;
}
/* Sends a signal to a currently-native thread. dcontext can be NULL. */
static void
execute_native_handler(dcontext_t *dcontext, int sig, sigframe_rt_t *our_frame)
{
/* If dcontext is NULL, we use a synthetic info struct where we fill in the
* info we need: the app's sigaction and sigstack settings.
*/
thread_sig_info_t synthetic = {};
kernel_sigaction_t *sigact_array[SIGARRAY_SIZE] = {};
bool intercept_array[SIGARRAY_SIZE];
kernel_sigaction_t sigact_struct;
thread_sig_info_t *info;
LOG(THREAD, LOG_ASYNCH, 2, "%s for signal %d in thread " TIDFMT "\n", __FUNCTION__,
sig, get_sys_thread_id());
if (dcontext != NULL) {
info = (thread_sig_info_t *)dcontext->signal_field;
} else {
if (atomic_read_bool(&multiple_handlers_present)) {
/* See i#1921 comment up top: we don't handle this. */
report_unhandleable_signal_and_exit(sig, "multiple native handlers");
ASSERT_NOT_REACHED();
}
info = &synthetic;
synthetic.we_intercept = intercept_array;
synthetic.we_intercept[sig] = true;
synthetic.app_sigaction = sigact_array;
synthetic.app_sigaction[sig] = &sigact_struct;
d_r_read_lock(&detached_sigact_lock);
memcpy(&sigact_struct, &detached_sigact[sig], sizeof(sigact_struct));
d_r_read_unlock(&detached_sigact_lock);
#ifdef HAVE_SIGALTSTACK
IF_DEBUG(int rc =)
sigaltstack_syscall(NULL, &synthetic.app_sigstack);
ASSERT(rc == 0);
#endif
}
kernel_ucontext_t *uc = get_ucontext_from_rt_frame(our_frame);
sigcontext_t *sc = SIGCXT_FROM_UCXT(uc);
kernel_sigset_t blocked;
byte *xsp = get_sigstack_frame_ptr(dcontext, info, sig, our_frame);
/* We do not send the signal to clients as it is not a managed thread and we
* cannot really take any action except send it to the native handler.
*/
if (info->app_sigaction[sig] == NULL ||
info->app_sigaction[sig]->handler == (handler_t)SIG_DFL) {
/* TODO i#1921: We want to call:
* execute_default_from_cache(dcontext, sig, our_frame, sc, false)
* But we need to pass in our sythentic info.
* And not all that callee's code will work with a NULL dcontext yet: it needs
* some work.
* Plus it calls handle_free() which we need to arrange for.
* For now we bail.
*/
if (can_always_delay[sig]) {
/* We are dropping asynchronous signals during detach. The thread may
* already have lost its TLS (xref i#3535). A safe read may result in a
* crash if DR's SIGSEGV handler is removed before the safe read's
* SIGSEGV is delivered.
*
* Note that besides dropping potentially important signals, there is
* still a small race window if the signal gets delivered after the
* detach has finished, i.e. doing detach is false. This is an issue in
* particular if the app has started re-attaching.
*
* Signals that are not clearly asynchronous may hit corner case(s) of
* i#3535. (xref i#26).
*/
if (doing_detach) {
DOLOG(1, LOG_ASYNCH, { dump_sigcontext(GLOBAL_DCONTEXT, sc); });
SYSLOG_INTERNAL_ERROR(
"Signal in native thread during detach with default action is not "
"fully supported: dropping and continuing (tid=%d, sig=%d)",
get_sys_thread_id(), sig);
}
return; /* Just drop the signal. */
}
if (default_action[sig] == DEFAULT_IGNORE)
return;
report_unhandleable_signal_and_exit(sig, "default action in native thread");
ASSERT_NOT_REACHED();
return;
}
ASSERT(info->app_sigaction[sig] != NULL &&
info->app_sigaction[sig]->handler != (handler_t)SIG_IGN &&
info->app_sigaction[sig]->handler != (handler_t)SIG_DFL);
RSTATS_INC(num_signals);
RSTATS_INC(num_native_signals);
report_app_problem(dcontext, APPFAULT_FAULT, (byte *)sc->SC_XIP, (byte *)sc->SC_FP,
"\nSignal %d delivered to application handler.\n", sig);
copy_frame_to_stack(dcontext, info, sig, our_frame, (void *)xsp, false /*!pending*/);
blocked = info->app_sigaction[sig]->mask;
if (!TEST(SA_NOMASK, (info->app_sigaction[sig]->flags)))
kernel_sigaddset(&blocked, sig);
DOLOG(2, LOG_ASYNCH, {
LOG(THREAD, LOG_ASYNCH, 3, "Pre-signal blocked signals, stored in our frame:\n");
dump_sigset(dcontext, (kernel_sigset_t *)&our_frame->uc.uc_sigmask);
});
for (int i = 1; i <= MAX_SIGNUM; i++) {
if (kernel_sigismember(&blocked, i)) {
kernel_sigaddset((kernel_sigset_t *)&our_frame->uc.uc_sigmask, i);
}
}
DOLOG(2, LOG_ASYNCH, {
LOG(THREAD, LOG_ASYNCH, 3, "Blocked signals within app handler will be:\n");
dump_sigset(dcontext, (kernel_sigset_t *)&our_frame->uc.uc_sigmask);
});
/* Now edit the resumption point when master_signal_handler returns to go
* straight to the app handler.
*/
sc->SC_XSP = (ptr_uint_t)xsp;
/* Set up args to handler. */
#ifdef X86_64
sc->SC_XDI = sig;
sc->SC_XSI = (reg_t) & ((sigframe_rt_t *)xsp)->info;
sc->SC_XDX = (reg_t) & ((sigframe_rt_t *)xsp)->uc;
#elif defined(AARCHXX)
sc->SC_R0 = sig;
if (IS_RT_FOR_APP(info, sig)) {
sc->SC_R1 = (reg_t) & ((sigframe_rt_t *)xsp)->info;
sc->SC_R2 = (reg_t) & ((sigframe_rt_t *)xsp)->uc;
}
if (sig_has_restorer(info, sig))
sc->SC_LR = (reg_t)info->app_sigaction[sig]->restorer;
else
sc->SC_LR = (reg_t)dynamorio_sigreturn;
#endif
sc->SC_XIP = (reg_t)SIGACT_PRIMARY_HANDLER(info->app_sigaction[sig]);
LOG(THREAD, LOG_ASYNCH, 2, "%s: set pc to handler %p with xsp=%p\n", __FUNCTION__,
SIGACT_PRIMARY_HANDLER(info->app_sigaction[sig]), xsp);
if (TEST(SA_ONESHOT, detached_sigact[sig].flags)) {
/* XXX: When we remove DR's handler from the final thread we'll ignore this
* delivery: but we can't safely access that data struct here. The best we
* can do is avoid delivering twice ourselves.
*/
d_r_write_lock(&detached_sigact_lock);
memset(&detached_sigact[sig], 0, sizeof(detached_sigact[sig]));
d_r_write_unlock(&detached_sigact_lock);
}
}
/* The arg to SYS_kill, i.e., the signal number, should be in dcontext->sys_param0 */
/* This routine unblocks signals, but the caller must set the handler to default. */
static void
terminate_via_kill(dcontext_t *dcontext)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
ASSERT(dcontext == get_thread_private_dcontext());
/* Enure signal_thread_exit() will not re-block */
memset(&info->app_sigblocked, 0, sizeof(info->app_sigblocked));
/* FIXME PR 541760: there can be multiple thread groups and thus
* this may not exit all threads in the address space
*/
block_cleanup_and_terminate(
dcontext, SYS_kill,
/* Pass -pid in case main thread has exited
* in which case will get -ESRCH
*/
IF_VMX86(os_in_vmkernel_userworld() ? -(int)get_process_id() :) get_process_id(),
dcontext->sys_param0, true, 0, 0);
ASSERT_NOT_REACHED();
}
bool
is_currently_on_sigaltstack(dcontext_t *dcontext)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
byte *cur_esp;
GET_STACK_PTR(cur_esp);
return (cur_esp >= (byte *)info->sigstack.ss_sp &&
cur_esp < (byte *)info->sigstack.ss_sp + info->sigstack.ss_size);
}
static void
terminate_via_kill_from_anywhere(dcontext_t *dcontext, int sig)
{
dcontext->sys_param0 = sig; /* store arg to SYS_kill */
if (is_currently_on_sigaltstack(dcontext)) {
/* We can't clean up our sigstack properly when we're on it
* (i#1160) so we terminate on the dstack.
*/
call_switch_stack(dcontext, dcontext->dstack,
(void (*)(void *))terminate_via_kill, NULL /*!d_r_initstack */,
false /*no return */);
} else {
terminate_via_kill(dcontext);
}
ASSERT_NOT_REACHED();
}
/* xref os_request_fatal_coredump() */
void
os_terminate_via_signal(dcontext_t *dcontext, terminate_flags_t flags, int sig)
{
if (signal_is_interceptable(sig)) {
bool set_action = false;
#if defined(STATIC_LIBRARY) && defined(LINUX)
if (INTERNAL_OPTION(invoke_app_on_crash)) {
/* We come here for asserts. Faults already bypass this routine. */
dcontext_t *my_dc = get_thread_private_dcontext();
if (my_dc != NULL) {
thread_sig_info_t *info = (thread_sig_info_t *)my_dc->signal_field;
if (info != NULL && info->app_sigaction[sig] != NULL &&
IS_RT_FOR_APP(info, sig)) {
set_action = true;
sigaction_syscall(sig, info->app_sigaction[sig], NULL);
}
}
}
#endif
if (!set_action) {
DEBUG_DECLARE(bool res =)
set_default_signal_action(sig);
ASSERT(res);
}
}
if (TEST(TERMINATE_CLEANUP, flags)) {
/* we enter from several different places, so rewind until top-level kstat */
KSTOP_REWIND_UNTIL(thread_measured);
ASSERT(dcontext != NULL);
dcontext->sys_param0 = sig;
/* XXX: the comment in the else below implies some systems have SYS_kill
* of SIGSEGV w/ no handler on oneself actually return.
* cleanup_and_terminate won't return to us and will use global_do_syscall
* to invoke SYS_kill, which in debug will do an inf loop (good!) but
* in release will do SYS_exit_group -- oh well, the systems I'm testing
* on do an immediate exit.
*/
terminate_via_kill_from_anywhere(dcontext, sig);
} else {
/* general clean up is unsafe: just remove .1config file */
d_r_config_exit();
dynamorio_syscall(SYS_kill, 2, get_process_id(), sig);
/* We try both the SYS_kill and the immediate crash since on some platforms
* the SIGKILL is delayed and on others the *-1 is hanging(?): should investigate
*/
if (sig == SIGSEGV) /* make doubly-sure */
*((int *)PTR_UINT_MINUS_1) = 0;
while (true) {
/* in case signal delivery is delayed we wait...forever */
os_thread_yield();
}
}
ASSERT_NOT_REACHED();
}
static bool
execute_default_action(dcontext_t *dcontext, int sig, sigframe_rt_t *frame,
sigcontext_t *sc_orig, bool from_dispatch, bool forged)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
sigcontext_t *sc = get_sigcontext_from_rt_frame(frame);
byte *pc = (byte *)sc->SC_XIP;
LOG(THREAD, LOG_ASYNCH, 3, "execute_default_action for signal %d\n", sig);
/* should only come here for signals we catch, or signal with ONESHOT
* that didn't sigreturn
*/
ASSERT(info->we_intercept[sig] ||
(info->app_sigaction[sig]->flags & SA_ONESHOT) != 0);
if (info->app_sigaction[sig] != NULL &&
(info->app_sigaction[sig]->flags & SA_ONESHOT) != 0) {
if (!info->we_intercept[sig]) {
handler_free(dcontext, info->app_sigaction[sig], sizeof(kernel_sigaction_t));
info->app_sigaction[sig] = NULL;
}
}
/* FIXME PR 205310: we can't always perfectly emulate the default
* behavior. To execute the default action, we have to un-register our
* handler, if we have one, for signals whose default action is not
* ignore or that will just be re-raised upon returning to the
* interrupted context -- FIXME: are any of the ignores repeated?
* SIGURG?
*
* If called from execute_handler_from_cache(), our master_signal_handler()
* is going to return directly to the translated context: which means we
* go native to re-execute the instr, which if it does in fact generate
* the signal again means we have a nice transparent core dump.
*
* If called from execute_handler_from_dispatch(), we need to generate
* the signal ourselves.
*/
if (default_action[sig] != DEFAULT_IGNORE) {
DEBUG_DECLARE(bool ok =)
set_default_signal_action(sig);
ASSERT(ok);
/* FIXME: to avoid races w/ shared handlers should set a flag to
* prevent another thread from re-enabling.
* Perhaps worse: what if this signal arrives for another thread
* in the meantime (and the default is not terminate)?
*/
if (info->shared_app_sigaction) {
LOG(THREAD, LOG_ASYNCH, 1,
"WARNING: having to install SIG_DFL for thread " TIDFMT ", but will be "
"shared!\n",
d_r_get_thread_id());
}
if (default_action[sig] == DEFAULT_TERMINATE ||
default_action[sig] == DEFAULT_TERMINATE_CORE) {
report_app_problem(dcontext, APPFAULT_CRASH, pc, (byte *)sc->SC_FP,
"\nSignal %d delivered to application as default "
"action.\n",
sig);
/* App may call sigaction to set handler SIG_DFL (unnecessary but legal),
* in which case DR will put a handler in info->app_sigaction[sig].
* We must clear it, otherwise, signal_thread_exit may cleanup the
* handler and set it to SIG_IGN instead.
*/
if (info->app_sigaction[sig] != NULL) {
ASSERT(info->we_intercept[sig]);
handler_free(dcontext, info->app_sigaction[sig],
sizeof(kernel_sigaction_t));
info->app_sigaction[sig] = NULL;
}
/* N.B.: we don't have to restore our handler because the
* default action is for the process (entire thread group for NPTL) to die!
*/
if (from_dispatch || can_always_delay[sig] || forged ||
is_sys_kill(dcontext, pc, (byte *)sc->SC_XSP, &frame->info)) {
/* This must have come from SYS_kill rather than raised by
* a faulting instruction. Thus we can't go re-execute the
* instr in order to re-raise the signal (if from_dispatch,
* we delayed and can't re-execute anyway). Instead we
* re-generate via SYS_kill. An alternative, if we don't
* care about generating a core dump, is to use SYS_exit
* and pass the right exit code to indicate the signal
* number: that would avoid races w/ the sigaction.
*
* FIXME: should have app make the syscall to get a more
* transparent core dump!
*/
LOG(THREAD, LOG_ASYNCH, 1, "Terminating via kill\n");
if (!from_dispatch && !forged)
KSTOP_NOT_MATCHING_NOT_PROPAGATED(fcache_default);
KSTOP_NOT_MATCHING_NOT_PROPAGATED(dispatch_num_exits);
if (is_couldbelinking(dcontext)) /* won't be for SYS_kill (i#1159) */
enter_nolinking(dcontext, NULL, false);
/* we could be on sigstack so call this version: */
terminate_via_kill_from_anywhere(dcontext, sig);
ASSERT_NOT_REACHED();
} else {
/* We assume that re-executing the interrupted instr will
* re-raise the fault. We could easily be wrong:
* xref PR 363811 infinite loop due to memory we
* thought was unreadable and thus thought would raise
* a signal; xref PR 368277 to improve is_sys_kill(), and the
* "forged" parameter that puts us in the if() above.
* FIXME PR 205310: we should check whether we come out of
* the cache when we expected to terminate!
*
* An alternative is to abandon transparent core dumps and
* do the same explicit SYS_kill we do for from_dispatch.
* That would let us clean up DR as well.
* FIXME: currently we do not clean up DR for a synchronous
* signal death, but we do for asynch.
*/
/* i#552: cleanup and raise client exit event */
int instr_sz = 0;
thread_sig_info_t *info;
/* We are on the sigstack now, so assign it to NULL to avoid being
* freed during process exit cleanup
*/
info = (thread_sig_info_t *)dcontext->signal_field;
info->sigstack.ss_sp = NULL;
/* We enter from several different places, so rewind until
* top-level kstat.
*/
KSTOP_REWIND_UNTIL(thread_measured);
/* We try to raise the same signal in app's context so a correct
* coredump can be generated. However, the client might change
* the code in a way that the corresponding app code won't
* raise the signal, so we first check if the app instr is the
* same as instr in the cache, and raise the signal (by return).
* Otherwise, we kill the process instead.
* XXX: if the PC is unreadable we'll just crash here...should check
* for readability safely.
*/
ASSERT(sc_orig != NULL);
instr_sz = decode_sizeof(dcontext, (byte *)sc_orig->SC_XIP,
NULL _IF_X86_64(NULL));
if (instr_sz != 0 &&
pc != NULL && /* avoid crash on xl8 failure (i#1699) */
instr_sz == decode_sizeof(dcontext, pc, NULL _IF_X86_64(NULL)) &&
memcmp(pc, (byte *)sc_orig->SC_XIP, instr_sz) == 0) {
/* the app instr matches the cache instr; cleanup and raise the
* the signal in the app context
*/
LOG(THREAD, LOG_ASYNCH, 1, "Raising signal by re-executing\n");
dynamo_process_exit();
/* we cannot re-enter the cache, which is freed by now */
ASSERT(!from_dispatch);
return false;
} else {
/* mismatch, cleanup and terminate */
LOG(THREAD, LOG_ASYNCH, 1, "Terminating via kill\n");
dcontext->sys_param0 = sig;
terminate_via_kill(dcontext);
ASSERT_NOT_REACHED();
}
}
} else {
/* FIXME PR 297033: in order to intercept DEFAULT_STOP /
* DEFAULT_CONTINUE signals, we need to set sigcontext to point
* to some kind of regain-control routine, so that when our
* thread gets to run again we can reset our handler. So far
* we have no signals that fall here that we intercept.
*/
CLIENT_ASSERT(false, "STOP/CONT signals not supported");
}
#if defined(DEBUG) && defined(INTERNAL)
if (sig == SIGSEGV && !dynamo_exited) {
/* pc should be an app pc at this point (it was translated) --
* check for bad cases here
*/
if (safe_is_in_fcache(dcontext, pc, (byte *)sc->SC_XSP)) {
fragment_t wrapper;
fragment_t *f;
LOG(THREAD, LOG_ALL, 1,
"Received SIGSEGV at pc " PFX " in thread " TIDFMT "\n", pc,
d_r_get_thread_id());
f = fragment_pclookup(dcontext, pc, &wrapper);
if (f)
disassemble_fragment(dcontext, f, false);
ASSERT_NOT_REACHED();
} else if (in_generated_routine(dcontext, pc)) {
LOG(THREAD, LOG_ALL, 1,
"Received SIGSEGV at generated non-code-cache pc " PFX "\n", pc);
ASSERT_NOT_REACHED();
}
}
#endif
}
/* now continue at the interruption point and re-raise the signal */
return true;
}
static bool
execute_default_from_cache(dcontext_t *dcontext, int sig, sigframe_rt_t *frame,
sigcontext_t *sc_orig, bool forged)
{
return execute_default_action(dcontext, sig, frame, sc_orig, false, forged);
}
static void
execute_default_from_dispatch(dcontext_t *dcontext, int sig, sigframe_rt_t *frame)
{
execute_default_action(dcontext, sig, frame, NULL, true, false);
}
void
receive_pending_signal(dcontext_t *dcontext)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
sigpending_t *temp;
int sig;
LOG(THREAD, LOG_ASYNCH, 3, "receive_pending_signal\n");
if (info->interrupted != NULL) {
relink_interrupted_fragment(dcontext, info);
}
/* grab first pending signal
* XXX: start with real-time ones?
*/
/* "lock" the array to prevent a new signal that interrupts this bit of
* code from prepended or deleting from the array while we're accessing it
*/
info->accessing_sigpending = true;
/* barrier to prevent compiler from moving the above write below the loop */
__asm__ __volatile__("" : : : "memory");
if (!info->multiple_pending_units &&
info->num_pending + 2 >= DYNAMO_OPTION(max_pending_signals)) {
/* We're close to the limit: proactively get a new unit while it's safe
* to acquire locks. We do that by pushing over the edge.
* We assume that filling up a 2nd unit is too pathological to plan for.
*/
info->multiple_pending_units = true;
SYSLOG_INTERNAL_WARNING("many pending signals: asking for 2nd special unit");
sigpending_t *temp1 = special_heap_alloc(info->sigheap);
sigpending_t *temp2 = special_heap_alloc(info->sigheap);
sigpending_t *temp3 = special_heap_alloc(info->sigheap);
special_heap_free(info->sigheap, temp1);
special_heap_free(info->sigheap, temp2);
special_heap_free(info->sigheap, temp3);
}
for (sig = 1; sig <= MAX_SIGNUM; sig++) {
if (info->sigpending[sig] != NULL) {
bool executing = true;
/* We do not re-check whether blocked if it was unblocked at
* receive time, to properly handle sigsuspend (i#1340).
*/
if (!info->sigpending[sig]->unblocked &&
kernel_sigismember(&info->app_sigblocked, sig)) {
LOG(THREAD, LOG_ASYNCH, 3, "\tsignal %d is blocked!\n", sig);
continue;
}
LOG(THREAD, LOG_ASYNCH, 3, "\treceiving signal %d\n", sig);
/* execute_handler_from_dispatch()'s call to copy_frame_to_stack() is
* allowed to remove the front entry from info->sigpending[sig] and
* jump to d_r_dispatch.
*/
executing = execute_handler_from_dispatch(dcontext, sig);
temp = info->sigpending[sig];
info->sigpending[sig] = temp->next;
special_heap_free(info->sigheap, temp);
info->num_pending--;
/* only one signal at a time! */
if (executing) {
/* Make negative so our fcache_enter check makes progress but
* our C code still considers there to be pending signals.
*/
dcontext->signals_pending = -1;
break;
}
}
}
/* barrier to prevent compiler from moving the below write above the loop */
__asm__ __volatile__("" : : : "memory");
info->accessing_sigpending = false;
/* we only clear this on a call to us where we find NO pending signals */
if (sig > MAX_SIGNUM) {
LOG(THREAD, LOG_ASYNCH, 3, "\tclearing signals_pending flag\n");
dcontext->signals_pending = 0;
}
}
/* Returns false if should NOT issue syscall. */
bool
#ifdef LINUX
handle_sigreturn(dcontext_t *dcontext, bool rt)
#else
handle_sigreturn(dcontext_t *dcontext, void *ucxt_param, int style)
#endif
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
sigcontext_t *sc = NULL; /* initialize to satisfy Mac clang */
kernel_ucontext_t *ucxt = NULL;
int sig = 0;
app_pc next_pc;
#if defined(DEBUG) || !defined(MACOS64)
/* xsp was put in mcontext prior to pre_system_call() */
reg_t xsp = get_mcontext(dcontext)->xsp;
#endif
#ifdef MACOS
bool rt = true;
#endif
LOG(THREAD, LOG_ASYNCH, 3, "%ssigreturn()\n", rt ? "rt_" : "");
LOG(THREAD, LOG_ASYNCH, 3, "\txsp is " PFX "\n", xsp);
#ifdef PROGRAM_SHEPHERDING
/* if (!sig_has_restorer, region was never added to exec list,
* allowed as pattern only and kicked off at first write via
* selfmod detection or otherwise if vsyscall, so no worries
* about having to remove it here
*/
#endif
/* The easiest way to set all the non-GPR state that DR does not separately
* preserve is to actually execute the sigreturn syscall, so we set up to do
* that. We do not want to change DR's signal state, however, so we set it
* back to DR's values after processing the state for the app.
*/
kernel_sigset_t our_mask;
sigprocmask_syscall(SIG_SETMASK, NULL, &our_mask, sizeof(our_mask));
/* get sigframe: it's the top thing on the stack, except the ret
* popped off pretcode.
* WARNING: handler for tcsh's window_change (SIGWINCH) clobbers its
* signal # arg, so don't use frame->sig! (kernel doesn't look at sig
* so app can get away with it)
*/
if (rt) {
#ifdef LINUX
sigframe_rt_t *frame = (sigframe_rt_t *)(xsp IF_X86(-sizeof(char *)));
/* use si_signo instead of sig, less likely to be clobbered by app */
sig = frame->info.si_signo;
# ifdef X86_32
LOG(THREAD, LOG_ASYNCH, 3, "\tsignal was %d (did == param %d)\n", sig,
frame->sig);
if (frame->sig != sig)
LOG(THREAD, LOG_ASYNCH, 1, "WARNING: app sig handler clobbered sig param\n");
# endif
sc = get_sigcontext_from_app_frame(info, sig, (void *)frame);
ucxt = &frame->uc;
/* Check again for the magic words. See the i#3812 comment above. */
IF_X86(ASSERT(
(sc->fpstate->sw_reserved.magic1 == 0 &&
sc->fpstate->sw_reserved.extended_size == sizeof(kernel_fpstate_t)) ||
(sc->fpstate->sw_reserved.magic1 == FP_XSTATE_MAGIC1 &&
*(int *)((byte *)sc->fpstate + sc->fpstate->sw_reserved.extended_size -
FP_XSTATE_MAGIC2_SIZE) == FP_XSTATE_MAGIC2)));
#elif defined(MACOS)
ucxt = (kernel_ucontext_t *)ucxt_param;
if (ucxt == NULL) {
/* On Mac the kernel seems to store state on whether the process is
* on the altstack, so longjmp calls _sigunaltstack() which issues a
* sigreturn syscall telling the kernel about the altstack change,
* with a NULL context.
*/
LOG(THREAD, LOG_ASYNCH, 3, "\tsigunalstack sigreturn: no context\n");
return true;
}
# ifdef X64
kernel_siginfo_t *siginfo = (kernel_siginfo_t *)ucxt - 1;
sig = siginfo->si_signo;
# else
/* The initial frame fields on the stack are messed up due to
* params to handler from tramp, so use params to syscall.
* XXX: we don't have signal # though: so we have to rely on app
* not clobbering the sig param field.
*/
sig = *(int *)xsp;
# endif
LOG(THREAD, LOG_ASYNCH, 3, "\tsignal was %d\n", sig);
sc = SIGCXT_FROM_UCXT(ucxt);
#endif
ASSERT(sig > 0 && sig <= MAX_SIGNUM && IS_RT_FOR_APP(info, sig));
/* Re-set sigstack from the value stored in the frame. Silently ignore failure,
* just like the kernel does.
*/
uint ignored;
/* The kernel checks for being on the stack *after* swapping stacks, so pass
* sc->SC_XSP as the current stack.
*/
handle_sigaltstack(dcontext, &ucxt->uc_stack, NULL, sc->SC_XSP, &ignored);
/* Restore DR's so sigreturn syscall won't change it. */
ucxt->uc_stack = info->sigstack;
/* FIXME: what if handler called sigaction and requested rt
* when itself was non-rt?
*/
/* Discard blocked signals, re-set from prev mask stored in frame. */
set_blocked(dcontext, SIGMASK_FROM_UCXT(ucxt), true /*absolute*/);
/* Restore DR's so sigreturn syscall won't change it. */
*SIGMASK_FROM_UCXT(ucxt) = our_mask;
}
#if defined(LINUX) && !defined(X64)
else {
/* FIXME: libc's restorer pops prior to calling sigreturn, I have
* no idea why, but kernel asks for xsp-8 not xsp-4...weird!
*/
kernel_sigset_t prevset;
sigframe_plain_t *frame = (sigframe_plain_t *)(xsp IF_X86(-8));
/* We don't trust frame->sig (app sometimes clobbers it), and for
* plain frame there's no other place that sig is stored,
* so as a hack we added a new frame!
* FIXME: this means we won't support nonstandard use of SYS_sigreturn,
* e.g., as NtContinue, if frame didn't come from a real signal and so
* wasn't copied to stack by us.
*/
sig = frame->sig_noclobber;
LOG(THREAD, LOG_ASYNCH, 3, "\tsignal was %d (did == param %d)\n", sig,
IF_X86_ELSE(frame->sig, 0));
# ifdef X86_32
if (frame->sig != sig)
LOG(THREAD, LOG_ASYNCH, 1, "WARNING: app sig handler clobbered sig param\n");
# endif
ASSERT(sig > 0 && sig <= MAX_SIGNUM && !IS_RT_FOR_APP(info, sig));
sc = get_sigcontext_from_app_frame(info, sig, (void *)frame);
/* discard blocked signals, re-set from prev mask stored in frame */
prevset.sig[0] = frame->IF_X86_ELSE(sc.oldmask, uc.uc_mcontext.oldmask);
if (_NSIG_WORDS > 1) {
memcpy(&prevset.sig[1], &frame->IF_X86_ELSE(extramask, uc.sigset_ex),
sizeof(prevset.sig[1]));
}
# ifdef ARM
ucxt = &frame->uc; /* we leave ucxt NULL for x86: not needed there */
# endif
set_blocked(dcontext, &prevset, true /*absolute*/);
/* Restore DR's so sigreturn syscall won't change it. */
convert_rt_mask_to_nonrt(frame, &our_mask);
}
#endif /* LINUX */
/* Make sure we deliver pending signals that are now unblocked.
*/
check_signals_pending(dcontext, info);
/* if we were building a trace, kill it */
if (is_building_trace(dcontext)) {
LOG(THREAD, LOG_ASYNCH, 3, "\tsquashing trace-in-progress\n");
trace_abort(dcontext);
}
/* Defensively check for NULL.
* XXX i#3182: It did happen but it is not clear how.
*/
if (info->app_sigaction[sig] != NULL &&
TEST(SA_ONESHOT, info->app_sigaction[sig]->flags)) {
ASSERT(info->app_sigaction[sig]->handler == (handler_t)SIG_DFL);
if (!info->we_intercept[sig]) {
/* let kernel do default independent of us */
handler_free(dcontext, info->app_sigaction[sig], sizeof(kernel_sigaction_t));
info->app_sigaction[sig] = NULL;
}
}
ASSERT(!safe_is_in_fcache(dcontext, (app_pc)sc->SC_XIP, (byte *)sc->SC_XSP));
#ifdef CLIENT_INTERFACE
sig_full_cxt_t sc_full = { sc, NULL /*not provided*/ };
get_mcontext(dcontext)->pc = dcontext->next_tag;
instrument_kernel_xfer(dcontext, DR_XFER_SIGNAL_RETURN, osc_empty, NULL,
get_mcontext(dcontext), (app_pc)sc->SC_XIP, sc->SC_XSP,
sc_full, NULL, sig);
#endif
#ifdef DEBUG
if (d_r_stats->loglevel >= 3 && (d_r_stats->logmask & LOG_ASYNCH) != 0) {
LOG(THREAD, LOG_ASYNCH, 3, "returning-to sigcontext " PFX ":\n", sc);
dump_sigcontext(dcontext, sc);
}
#endif
/* XXX i#1206: if we interrupted a non-ignorable syscall to run the app's
* handler, and we set up to restart the syscall, we'll come here with the
* translated syscall pc -- thus we can't distinguish from a signal interrupting
* the prior app instr. So we can't simply point at do_syscall and call
* set_at_syscall -- we have to re-interpret the syscall and re-run the
* pre-syscall handler. Hopefully all our pre-syscall handlers can handle that.
*/
/* set up for d_r_dispatch */
/* we have to use a different slot since next_tag ends up holding the do_syscall
* entry when entered from d_r_dispatch (we're called from
* pre_syscall, prior to entering cache)
*/
dcontext->asynch_target = canonicalize_pc_target(
dcontext, (app_pc)(sc->SC_XIP IF_ARM(| (TEST(EFLAGS_T, sc->SC_XFLAGS) ? 1 : 0))));
next_pc = dcontext->asynch_target;
#ifdef VMX86_SERVER
/* PR 404712: kernel only restores gp regs so we do it ourselves and avoid
* complexities of kernel's non-linux-like sigreturn semantics
*/
sig_full_cxt_t sc_full = { sc, NULL }; /* non-ARM so NULL ok */
sigcontext_to_mcontext(get_mcontext(dcontext), &sc_full, DR_MC_ALL);
#else
/* HACK to get eax put into mcontext AFTER do_syscall */
dcontext->next_tag = (app_pc)sc->IF_X86_ELSE(SC_XAX, SC_R0);
/* use special linkstub so we know why we came out of the cache */
sc->IF_X86_ELSE(SC_XAX, SC_R0) = (ptr_uint_t)get_asynch_linkstub();
/* set our sigreturn context to point to fcache_return */
/* We don't need PC_AS_JMP_TGT b/c the kernel uses EFLAGS_T for the mode */
sc->SC_XIP = (ptr_uint_t)fcache_return_routine(dcontext);
/* if we overlaid inner frame on nested signal, will end up with this
* error -- disable in release build since this is often app's fault (stack
* too small)
* FIXME: how make this transparent? what ends up happening is that we
* get a segfault when we start interpreting d_r_dispatch, we want to make it
* look like whatever would happen to the app...
*/
ASSERT((app_pc)sc->SC_XIP != next_pc);
# ifdef AARCHXX
ASSERT(get_sigcxt_stolen_reg(sc) != (reg_t)*get_dr_tls_base_addr());
/* We're called from DR and are not yet in the cache, so we want to set the
* mcontext slot, not the TLS slot, to set the stolen reg value.
*/
set_stolen_reg_val(get_mcontext(dcontext), get_sigcxt_stolen_reg(sc));
/* The linkstub expects DR's TLS to be in the actual register. */
set_sigcxt_stolen_reg(sc, (reg_t)*get_dr_tls_base_addr());
# ifdef AARCH64
/* On entry to the do_syscall gencode, we save X1 into TLS_REG1_SLOT.
* Then the sigreturn would redirect the flow to the fcache_return gencode.
* In fcache_return it recovers the values of x0 and x1 from TLS_SLOT 0 and 1.
*/
get_mcontext(dcontext)->r1 = sc->regs[1];
# else
/* We're going to our fcache_return gencode which uses DEFAULT_ISA_MODE */
set_pc_mode_in_cpsr(sc, DEFAULT_ISA_MODE);
# endif
# endif
#endif
LOG(THREAD, LOG_ASYNCH, 3, "set next tag to " PFX ", sc->SC_XIP to " PFX "\n",
next_pc, sc->SC_XIP);
return IF_VMX86_ELSE(false, true);
}
bool
is_signal_restorer_code(byte *pc, size_t *len)
{
/* is this a sigreturn pattern placed by kernel on the stack or vsyscall page?
* for non-rt frame:
* 0x58 popl %eax
* 0xb8 <sysnum> movl SYS_sigreturn, %eax
* 0xcd 0x80 int 0x80
* for rt frame:
* 0xb8 <sysnum> movl SYS_rt_sigreturn, %eax
* 0xcd 0x80 int 0x80
*/
/* optimized we only need two uint reads, but we have to do
* some little-endian byte-order reverses to get the right result
*/
#define reverse(x) \
((((x)&0xff) << 24) | (((x)&0xff00) << 8) | (((x)&0xff0000) >> 8) | \
(((x)&0xff000000) >> 24))
#ifdef MACOS
# define SYS_RT_SIGRET SYS_sigreturn
#else
# define SYS_RT_SIGRET SYS_rt_sigreturn
#endif
#ifndef X64
/* 58 b8 s4 s3 s2 s1 cd 80 */
static const uint non_rt_1w = reverse(0x58b80000 | (reverse(SYS_sigreturn) >> 16));
static const uint non_rt_2w = reverse((reverse(SYS_sigreturn) << 16) | 0xcd80);
#endif
/* b8 s4 s3 s2 s1 cd 80 XX */
static const uint rt_1w = reverse(0xb8000000 | (reverse(SYS_RT_SIGRET) >> 8));
static const uint rt_2w = reverse((reverse(SYS_RT_SIGRET) << 24) | 0x00cd8000);
/* test rt first as it's the most common
* only 7 bytes here so we ignore the last one (becomes msb since little-endian)
*/
if (*((uint *)pc) == rt_1w && (*((uint *)(pc + 4)) & 0x00ffffff) == rt_2w) {
if (len != NULL)
*len = 7;
return true;
}
#ifndef X64
if (*((uint *)pc) == non_rt_1w && *((uint *)(pc + 4)) == non_rt_2w) {
if (len != NULL)
*len = 8;
return true;
}
#endif
return false;
}
void
os_forge_exception(app_pc target_pc, dr_exception_type_t type)
{
/* PR 205136:
* We want to deliver now, and the caller expects us not to return.
* We have two alternatives:
* 1) Emulate stack frame, and call transfer_to_dispatch() for delivery. We
* may not know how to fill out every field of the frame (cr2, etc.). Plus,
* we have problems w/ default actions (PR 205310) but we have to solve
* those long-term anyway. We also have to create different frames based on
* whether app intercepts via rt or not.
* 2) Call SYS_tgkill from a special location that our handler can
* recognize and know it's a signal meant for the app and that the
* interrupted DR can be discarded. We'd then essentially repeat 1,
* but modifying the kernel-generated frame. We'd have to always
* intercept SIGILL.
* I'm going with #1 for now b/c the common case is simpler.
*/
dcontext_t *dcontext = get_thread_private_dcontext();
#if defined(LINUX) && defined(X86)
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
#endif
char frame_no_xstate[sizeof(sigframe_rt_t)];
sigframe_rt_t *frame = (sigframe_rt_t *)frame_no_xstate;
int sig;
dr_where_am_i_t cur_whereami = dcontext->whereami;
kernel_ucontext_t *uc = get_ucontext_from_rt_frame(frame);
sigcontext_t *sc = SIGCXT_FROM_UCXT(uc);
switch (type) {
case ILLEGAL_INSTRUCTION_EXCEPTION: sig = SIGILL; break;
case UNREADABLE_MEMORY_EXECUTION_EXCEPTION: sig = SIGSEGV; break;
case SINGLE_STEP_EXCEPTION: ASSERT_NOT_IMPLEMENTED(false); /* FIXME: i#2144 */
case IN_PAGE_ERROR_EXCEPTION: /* fall-through: Windows only */
default:
ASSERT_NOT_REACHED();
sig = SIGSEGV;
break;
}
LOG(GLOBAL, LOG_ASYNCH, 1, "os_forge_exception sig=%d\n", sig);
/* Since we always delay delivery, we always want an rt frame. we'll convert
* to a plain frame on delivery.
*/
memset(frame, 0, sizeof(*frame));
frame->info.si_signo = sig;
/* Set si_code to match what would happen natively. We also need this to
* avoid the !is_sys_kill() check in record_pending_signal() to avoid an
* infinite loop (i#3171).
*/
frame->info.si_code = IF_LINUX_ELSE(SI_KERNEL, 0);
frame->info.si_addr = target_pc;
#ifdef X86_32
frame->sig = sig;
frame->pinfo = &frame->info;
frame->puc = (void *)&frame->uc;
#endif
#if defined(LINUX) && defined(X86)
/* We use a TLS buffer to avoid too much stack space here. */
sc->fpstate = (kernel_fpstate_t *)get_and_initialize_xstate_buffer(dcontext);
#endif
mcontext_to_ucontext(uc, get_mcontext(dcontext));
sc->SC_XIP = (reg_t)target_pc;
/* We'll fill in fpstate at delivery time.
* We fill in segment registers to their current values and assume they won't
* change and that these are the right values.
*
* FIXME i#2095: restore the app's segment register value(s).
*
* XXX: it seems to work w/o filling in the other state:
* I'm leaving cr2 and other fields all zero.
* If this gets problematic we could switch to approach #2.
*/
thread_set_segment_registers(sc);
#if defined(X86) && defined(LINUX)
if (sig_has_restorer(info, sig))
frame->pretcode = (char *)info->app_sigaction[sig]->restorer;
else
frame->pretcode = (char *)dynamorio_sigreturn;
#endif
/* We assume that we do not need to translate the context when forged.
* If we did, we'd move this below enter_nolinking() (and update
* record_pending_signal() to do the translation).
*/
record_pending_signal(dcontext, sig, &frame->uc, frame,
true /*forged*/
_IF_CLIENT(NULL));
/* For most callers this is not necessary and we only do it to match
* the Windows usage model: but for forging from our own handler,
* this is good b/c it resets us to the base of dstack.
*/
/* tell d_r_dispatch() why we're coming there */
dcontext->whereami = DR_WHERE_TRAMPOLINE;
KSTART(dispatch_num_exits);
set_last_exit(dcontext, (linkstub_t *)get_asynch_linkstub());
if (is_couldbelinking(dcontext))
enter_nolinking(dcontext, NULL, false);
transfer_to_dispatch(
dcontext, get_mcontext(dcontext),
cur_whereami != DR_WHERE_FCACHE && cur_whereami != DR_WHERE_SIGNAL_HANDLER
/*full_DR_state*/);
ASSERT_NOT_REACHED();
}
void
os_request_fatal_coredump(const char *msg)
{
/* To enable getting a coredump just make sure that rlimits are
* not preventing getting one, e.g. ulimit -c unlimited
*/
SYSLOG_INTERNAL_ERROR("Crashing the process deliberately for a core dump!");
os_terminate_via_signal(NULL, 0 /*no cleanup*/, SIGSEGV);
ASSERT_NOT_REACHED();
}
void
os_request_live_coredump(const char *msg)
{
#ifdef VMX86_SERVER
if (os_in_vmkernel_userworld()) {
vmk_request_live_coredump(msg);
return;
}
#endif
LOG(GLOBAL, LOG_ASYNCH, 1,
"LiveCoreDump unsupported (PR 365105). "
"Continuing execution without a core.\n");
return;
}
void
os_dump_core(const char *msg)
{
/* FIXME Case 3408: fork stack dump crashes on 2.6 kernel, so moving the getchar
* ahead to aid in debugging */
if (TEST(DUMPCORE_WAIT_FOR_DEBUGGER, dynamo_options.dumpcore_mask)) {
SYSLOG_INTERNAL_ERROR("looping so you can use gdb to attach to pid %s",
get_application_pid());
IF_CLIENT_INTERFACE(SYSLOG(SYSLOG_CRITICAL, WAITING_FOR_DEBUGGER, 2,
get_application_name(), get_application_pid()));
/* getchar() can hit our own vsyscall hook (from PR 212570); typically we
* want to attach and not continue anyway, so doing an infinite loop:
*/
while (true)
os_thread_yield();
}
if (DYNAMO_OPTION(live_dump)) {
os_request_live_coredump(msg);
}
if (TEST(DUMPCORE_INCLUDE_STACKDUMP, dynamo_options.dumpcore_mask)) {
/* fork, dump core, then use gdb to get a stack dump
* we can get into an infinite loop if there's a seg fault
* in the process of doing this -- so we have a do-once test,
* and if it failed we do the no-symbols dr callstack dump
*/
static bool tried_stackdump = false;
if (!tried_stackdump) {
tried_stackdump = true;
d_r_stackdump();
} else {
static bool tried_calldump = false;
if (!tried_calldump) {
tried_calldump = true;
dump_dr_callstack(STDERR);
}
}
}
if (!DYNAMO_OPTION(live_dump)) {
os_request_fatal_coredump(msg);
ASSERT_NOT_REACHED();
}
}
#ifdef RETURN_AFTER_CALL
bool
at_known_exception(dcontext_t *dcontext, app_pc target_pc, app_pc source_fragment)
{
/* There is a known exception in signal restorers and the Linux
* dynamic symbol resoulution.
* The latter we assume it is the only other recurring known exception,
* so the first time we pattern match to help make sure it is indeed
* _dl_runtime_resolve (since with LD_BIND_NOW it will never be called).
* After that we compare with the known value.
*/
static app_pc known_exception = 0;
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
LOG(THREAD, LOG_INTERP, 1, "RCT: testing for KNOWN exception " PFX " " PFX "\n",
target_pc, source_fragment);
/* Check if this is a signal return.
FIXME: we should really get that from the frame itself.
Since currently grabbing restorer only when copying a frame,
this will work with nested signals only if they all have same restorer
(I haven't seen restorers other than the one in libc)
*/
if (target_pc == info->signal_restorer_retaddr) {
LOG(THREAD, LOG_INTERP, 1,
"RCT: KNOWN exception this is a signal restorer --ok \n");
STATS_INC(ret_after_call_signal_restorer);
return true;
}
if (source_fragment == known_exception) {
LOG(THREAD, LOG_INTERP, 1,
"RCT: KNOWN exception again _dl_runtime_resolve --ok\n");
return true;
}
if (known_exception == 0) {
int ret_imm;
return at_dl_runtime_resolve_ret(dcontext, source_fragment, &ret_imm);
}
return false;
}
#endif /* RETURN_AFTER_CALL */
/***************************************************************************
* ITIMERS
*
* We support combining an app itimer with a DR itimer for each of the 3 types
* (PR 204556).
*/
static inline uint64
timeval_to_usec(struct timeval *t1)
{
return ((uint64)(t1->tv_sec)) * 1000000 + t1->tv_usec;
}
static inline void
usec_to_timeval(uint64 usec, struct timeval *t1)
{
t1->tv_sec = (long)usec / 1000000;
t1->tv_usec = (long)usec % 1000000;
}
static void
init_itimer(dcontext_t *dcontext, bool first)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
int i;
ASSERT(info != NULL);
ASSERT(!info->shared_itimer); /* else inherit */
LOG(THREAD, LOG_ASYNCH, 2, "thread has private itimers%s\n",
os_itimers_thread_shared() ? " (for now)" : "");
if (os_itimers_thread_shared()) {
/* we have to allocate now even if no itimer is installed until later,
* so that all child threads point to the same data
*/
info->itimer = (thread_itimer_info_t(*)[NUM_ITIMERS])global_heap_alloc(
sizeof(*info->itimer) HEAPACCT(ACCT_OTHER));
} else {
/* for simplicity and parallel w/ shared we allocate proactively */
info->itimer = (thread_itimer_info_t(*)[NUM_ITIMERS])heap_alloc(
dcontext, sizeof(*info->itimer) HEAPACCT(ACCT_OTHER));
}
memset(info->itimer, 0, sizeof(*info->itimer));
for (i = 0; i < NUM_ITIMERS; i++) {
ASSIGN_INIT_RECURSIVE_LOCK_FREE((*info->itimer)[i].lock, shared_itimer_lock);
}
if (first) {
/* see if app has set up an itimer before we were loaded */
struct itimerval prev;
int rc;
int which;
for (which = 0; which < NUM_ITIMERS; which++) {
rc = getitimer_syscall(which, &prev);
ASSERT(rc == SUCCESS);
(*info->itimer)[which].app.interval = timeval_to_usec(&prev.it_interval);
(*info->itimer)[which].app.value = timeval_to_usec(&prev.it_value);
}
}
}
/* Up to caller to hold lock for shared itimers */
static bool
set_actual_itimer(dcontext_t *dcontext, int which, thread_sig_info_t *info, bool enable)
{
struct itimerval val;
int rc;
ASSERT(info != NULL && info->itimer != NULL);
ASSERT(which >= 0 && which < NUM_ITIMERS);
if (enable) {
LOG(THREAD, LOG_ASYNCH, 2,
"installing itimer %d interval=" INT64_FORMAT_STRING
", value=" INT64_FORMAT_STRING "\n",
which, (*info->itimer)[which].actual.interval,
(*info->itimer)[which].actual.value);
/* i#2907: we have no signal handlers until we start the app (i#2335)
* so we can't set up an itimer until then.
*/
ASSERT(dynamo_initialized);
ASSERT(!info->shared_itimer ||
self_owns_recursive_lock(&(*info->itimer)[which].lock));
usec_to_timeval((*info->itimer)[which].actual.interval, &val.it_interval);
usec_to_timeval((*info->itimer)[which].actual.value, &val.it_value);
} else {
LOG(THREAD, LOG_ASYNCH, 2, "disabling itimer %d\n", which);
memset(&val, 0, sizeof(val));
(*info->itimer)[which].actual.value = 0;
(*info->itimer)[which].actual.interval = 0;
}
rc = setitimer_syscall(which, &val, NULL);
return (rc == SUCCESS);
}
/* Caller should hold lock */
static bool
itimer_new_settings(dcontext_t *dcontext, int which, bool app_changed)
{
struct itimerval val;
bool res = true;
int rc;
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
ASSERT(info != NULL && info->itimer != NULL);
ASSERT(which >= 0 && which < NUM_ITIMERS);
ASSERT(!info->shared_itimer ||
self_owns_recursive_lock(&(*info->itimer)[which].lock));
/* the general strategy is to set the actual value to the smaller,
* update the larger on each signal, and when the larger becomes
* smaller do a one-time swap for the remaining
*/
if ((*info->itimer)[which].dr.interval > 0 &&
((*info->itimer)[which].app.interval == 0 ||
(*info->itimer)[which].dr.interval < (*info->itimer)[which].app.interval))
(*info->itimer)[which].actual.interval = (*info->itimer)[which].dr.interval;
else
(*info->itimer)[which].actual.interval = (*info->itimer)[which].app.interval;
if ((*info->itimer)[which].actual.value > 0) {
if ((*info->itimer)[which].actual.interval == 0 &&
(*info->itimer)[which].dr.value == 0 &&
(*info->itimer)[which].app.value == 0) {
(*info->itimer)[which].actual.value = 0;
res = set_actual_itimer(dcontext, which, info, false /*disabled*/);
} else {
/* one of app or us has an in-flight timer which we should not interrupt.
* but, we already set the new requested value (for app or us), so we
* need to update the actual value so we subtract properly.
*/
rc = getitimer_syscall(which, &val);
ASSERT(rc == SUCCESS);
uint64 left = timeval_to_usec(&val.it_value);
if (!app_changed &&
(*info->itimer)[which].actual.value == (*info->itimer)[which].app.value)
(*info->itimer)[which].app.value = left;
if (app_changed &&
(*info->itimer)[which].actual.value == (*info->itimer)[which].dr.value)
(*info->itimer)[which].dr.value = left;
(*info->itimer)[which].actual.value = left;
}
} else {
if ((*info->itimer)[which].dr.value > 0 &&
((*info->itimer)[which].app.value == 0 ||
(*info->itimer)[which].dr.value < (*info->itimer)[which].app.value))
(*info->itimer)[which].actual.value = (*info->itimer)[which].dr.value;
else {
(*info->itimer)[which].actual.value = (*info->itimer)[which].app.value;
}
res = set_actual_itimer(dcontext, which, info, true /*enable*/);
}
return res;
}
bool
set_itimer_callback(dcontext_t *dcontext, int which, uint millisec,
void (*func)(dcontext_t *, priv_mcontext_t *),
void (*func_api)(dcontext_t *, dr_mcontext_t *))
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
bool rc;
if (which < 0 || which >= NUM_ITIMERS) {
CLIENT_ASSERT(false, "invalid itimer type");
return false;
}
if (func == NULL && func_api == NULL && millisec != 0) {
CLIENT_ASSERT(false, "invalid function");
return false;
}
ASSERT(info != NULL && info->itimer != NULL);
if (info->shared_itimer)
acquire_recursive_lock(&(*info->itimer)[which].lock);
(*info->itimer)[which].dr.interval = ((uint64)millisec) * 1000;
(*info->itimer)[which].dr.value = (*info->itimer)[which].dr.interval;
(*info->itimer)[which].cb = func;
(*info->itimer)[which].cb_api = func_api;
if (!dynamo_initialized) {
/* i#2907: we have no signal handlers until we start the app (i#2335)
* so we can't set up an itimer until then. start_itimer() called
* from os_thread_under_dynamo() will enable it.
*/
LOG(THREAD, LOG_ASYNCH, 2, "delaying itimer until attach\n");
rc = true;
} else
rc = itimer_new_settings(dcontext, which, false /*us*/);
if (info->shared_itimer)
release_recursive_lock(&(*info->itimer)[which].lock);
return rc;
}
uint
get_itimer_frequency(dcontext_t *dcontext, int which)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
uint ms = 0;
if (which < 0 || which >= NUM_ITIMERS) {
CLIENT_ASSERT(false, "invalid itimer type");
return 0;
}
ASSERT(info != NULL && info->itimer != NULL);
if (info->shared_itimer)
acquire_recursive_lock(&(*info->itimer)[which].lock);
ms = (*info->itimer)[which].dr.interval / 1000;
if (info->shared_itimer)
release_recursive_lock(&(*info->itimer)[which].lock);
return ms;
}
static int
signal_to_itimer_type(int sig)
{
if (sig == SIGALRM)
return ITIMER_REAL;
else if (sig == SIGVTALRM)
return ITIMER_VIRTUAL;
else if (sig == SIGPROF)
return ITIMER_PROF;
else
return -1;
}
static bool
alarm_signal_has_DR_only_itimer(dcontext_t *dcontext, int signal)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
int which = signal_to_itimer_type(signal);
if (which == -1)
return false;
if (info->shared_itimer)
acquire_recursive_lock(&(*info->itimer)[which].lock);
bool DR_only =
((*info->itimer)[which].dr.value > 0 || (*info->itimer)[which].dr.interval > 0) &&
(*info->itimer)[which].app.value == 0 && (*info->itimer)[which].app.interval == 0;
if (info->shared_itimer)
release_recursive_lock(&(*info->itimer)[which].lock);
return DR_only;
}
static bool
handle_alarm(dcontext_t *dcontext, int sig, kernel_ucontext_t *ucxt)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
ASSERT(info != NULL && info->itimer != NULL);
int which = 0;
bool invoke_cb = false, pass_to_app = false, reset_timer_manually = false;
bool should_release_lock = false;
/* i#471: suppress alarms coming in after exit */
if (dynamo_exited)
return pass_to_app;
which = signal_to_itimer_type(sig);
ASSERT(which != -1);
LOG(THREAD, LOG_ASYNCH, 2, "received alarm %d @" PFX "\n", which,
SIGCXT_FROM_UCXT(ucxt)->SC_XIP);
/* This alarm could have interrupted an app thread making an itimer syscall,
* which is why we don't want to block on a lock here.
* It can't interrupt this same thread handling a prior alarm (b/c we block
* the signal in our handler). It could arrive in thread B while thread A
* is still handling a prior alarm if the alarm frequency is high and the
* processing is slow, which is why we split the locks to be per-itimer-type.
* We also avoid new thread setup code acquiring these itimer locks by using
* atomic increments instead for the refcounts. Xref i#2993.
*/
if (info->shared_itimer) {
#ifdef DEADLOCK_AVOIDANCE
/* i#2061: in debug build we can get an alarm while in deadlock handling
* code that holds innermost_lock. We just drop such alarms.
*/
if (OWN_MUTEX(&innermost_lock))
return pass_to_app;
#endif
if (self_owns_recursive_lock(&(*info->itimer)[which].lock)) {
/* What can we do? We just go ahead and hope conflicting writes work out.
* We don't re-acquire in case app was in middle of acquiring.
*/
} else {
#define ALARM_LOCK_MAX_TRIES 4
int i;
for (i = 0; i < ALARM_LOCK_MAX_TRIES; ++i) {
if (try_recursive_lock(&(*info->itimer)[which].lock)) {
should_release_lock = true;
break;
}
os_thread_yield();
}
if (!should_release_lock) {
/* Heuristic: if fail N times then assume interrupted lock routine
* while processing an app syscall (see above: we ruled out other
* scenarios). What can we do? Just continue and hope conflicting
* writes work out.
*/
}
}
}
if ((*info->itimer)[which].app.value > 0) {
/* Alarm could have been on its way when app value changed */
if ((*info->itimer)[which].app.value >= (*info->itimer)[which].actual.value) {
(*info->itimer)[which].app.value -= (*info->itimer)[which].actual.value;
LOG(THREAD, LOG_ASYNCH, 2, "\tapp value is now %d\n",
(*info->itimer)[which].app.value);
if ((*info->itimer)[which].app.value == 0) {
pass_to_app = true;
(*info->itimer)[which].app.value = (*info->itimer)[which].app.interval;
} else
reset_timer_manually = true;
}
}
if ((*info->itimer)[which].dr.value > 0) {
/* Alarm could have been on its way when DR value changed */
if ((*info->itimer)[which].dr.value >= (*info->itimer)[which].actual.value) {
(*info->itimer)[which].dr.value -= (*info->itimer)[which].actual.value;
LOG(THREAD, LOG_ASYNCH, 2, "\tdr value is now %d\n",
(*info->itimer)[which].dr.value);
if ((*info->itimer)[which].dr.value == 0) {
invoke_cb = true;
(*info->itimer)[which].dr.value = (*info->itimer)[which].dr.interval;
} else
reset_timer_manually = true;
}
}
/* for efficiency we let the kernel reset the value to interval if
* there's only one timer
*/
if (reset_timer_manually) {
(*info->itimer)[which].actual.value = 0;
itimer_new_settings(dcontext, which, true /*doesn't matter: actual.value==0*/);
} else
(*info->itimer)[which].actual.value = (*info->itimer)[which].actual.interval;
if (invoke_cb) {
/* invoke after setting new itimer value */
/* we save stack space by allocating superset dr_mcontext_t */
dr_mcontext_t dmc;
dr_mcontext_init(&dmc);
priv_mcontext_t *mc = dr_mcontext_as_priv_mcontext(&dmc);
ucontext_to_mcontext(mc, ucxt);
void (*cb)(dcontext_t *, priv_mcontext_t *) = (*info->itimer)[which].cb;
void (*cb_api)(dcontext_t *, dr_mcontext_t *) = (*info->itimer)[which].cb_api;
if (which == ITIMER_VIRTUAL && info->shared_itimer && should_release_lock) {
release_recursive_lock(&(*info->itimer)[which].lock);
should_release_lock = false;
}
if (cb != NULL) {
cb(dcontext, mc);
} else {
cb_api(dcontext, &dmc);
}
}
if (info->shared_itimer && should_release_lock)
release_recursive_lock(&(*info->itimer)[which].lock);
return pass_to_app;
}
/* Starts itimer if stopped, or increases refcount of existing itimer if already
* started. It is *not* safe to call this more than once for the same thread,
* since it will inflate the refcount and prevent cleanup.
*/
void
start_itimer(dcontext_t *dcontext)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
ASSERT(info != NULL && info->itimer != NULL);
bool start = false;
if (info->shared_itimer) {
/* i#2993: We avoid acquiring the lock as an alarm signal can arrive during
* the lock routine (esp in debug build) and cause problems.
*/
int new_count =
atomic_add_exchange_int((volatile int *)info->shared_itimer_underDR, 1);
start = (new_count == 1);
} else
start = true;
if (start) {
/* Enable all DR itimers b/c at least one thread in this set of threads
* sharing itimers is under DR control
*/
int which;
LOG(THREAD, LOG_ASYNCH, 2, "starting DR itimers from thread " TIDFMT "\n",
d_r_get_thread_id());
for (which = 0; which < NUM_ITIMERS; which++) {
if (info->shared_itimer)
acquire_recursive_lock(&(*info->itimer)[which].lock);
/* May have already been set up with the start delayed (i#2907). */
if ((*info->itimer)[which].dr.interval > 0) {
(*info->itimer)[which].dr.value = (*info->itimer)[which].dr.interval;
itimer_new_settings(dcontext, which, false /*!app*/);
}
if (info->shared_itimer)
release_recursive_lock(&(*info->itimer)[which].lock);
}
}
}
/* Decrements the itimer refcount, and turns off the itimer once there are no
* more threads listening for it. It is not safe to call this more than once on
* the same thread.
*/
void
stop_itimer(dcontext_t *dcontext)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
ASSERT(info != NULL && info->itimer != NULL);
bool stop = false;
if (info->shared_itimer) {
ASSERT(*info->shared_itimer_underDR > 0);
int new_count =
atomic_add_exchange_int((volatile int *)info->shared_itimer_underDR, -1);
stop = (new_count == 0);
} else
stop = true;
if (stop) {
/* Disable all DR itimers b/c this set of threads sharing this
* itimer is now completely native
*/
int which;
LOG(THREAD, LOG_ASYNCH, 2, "stopping DR itimers from thread " TIDFMT "\n",
d_r_get_thread_id());
for (which = 0; which < NUM_ITIMERS; which++) {
if (info->shared_itimer)
acquire_recursive_lock(&(*info->itimer)[which].lock);
if ((*info->itimer)[which].dr.value > 0) {
(*info->itimer)[which].dr.value = 0;
if ((*info->itimer)[which].app.value > 0) {
(*info->itimer)[which].actual.interval =
(*info->itimer)[which].app.interval;
} else
set_actual_itimer(dcontext, which, info, false /*disable*/);
}
if (info->shared_itimer)
release_recursive_lock(&(*info->itimer)[which].lock);
}
}
}
/* handle app itimer syscalls */
/* handle_pre_alarm also calls this function and passes NULL as prev_timer */
void
handle_pre_setitimer(dcontext_t *dcontext, int which, const struct itimerval *new_timer,
struct itimerval *prev_timer)
{
if (new_timer == NULL || which < 0 || which >= NUM_ITIMERS)
return;
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
ASSERT(info != NULL && info->itimer != NULL);
struct itimerval val;
if (d_r_safe_read(new_timer, sizeof(val), &val)) {
if (info->shared_itimer)
acquire_recursive_lock(&(*info->itimer)[which].lock);
/* save a copy in case the syscall fails */
(*info->itimer)[which].app_saved = (*info->itimer)[which].app;
(*info->itimer)[which].app.interval = timeval_to_usec(&val.it_interval);
(*info->itimer)[which].app.value = timeval_to_usec(&val.it_value);
LOG(THREAD, LOG_ASYNCH, 2,
"app setitimer type=%d interval=" SZFMT " value=" SZFMT "\n", which,
(*info->itimer)[which].app.interval, (*info->itimer)[which].app.value);
itimer_new_settings(dcontext, which, true /*app*/);
if (info->shared_itimer)
release_recursive_lock(&(*info->itimer)[which].lock);
}
}
void
handle_post_setitimer(dcontext_t *dcontext, bool success, int which,
const struct itimerval *new_timer, struct itimerval *prev_timer)
{
if (new_timer == NULL || which < 0 || which >= NUM_ITIMERS) {
ASSERT(new_timer == NULL || !success);
return;
}
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
ASSERT(info != NULL && info->itimer != NULL);
ASSERT(which >= 0 && which < NUM_ITIMERS);
if (!success && new_timer != NULL) {
if (info->shared_itimer)
acquire_recursive_lock(&(*info->itimer)[which].lock);
/* restore saved pre-syscall settings */
(*info->itimer)[which].app = (*info->itimer)[which].app_saved;
itimer_new_settings(dcontext, which, true /*app*/);
if (info->shared_itimer)
release_recursive_lock(&(*info->itimer)[which].lock);
}
if (success && prev_timer != NULL)
handle_post_getitimer(dcontext, success, which, prev_timer);
}
void
handle_post_getitimer(dcontext_t *dcontext, bool success, int which,
struct itimerval *cur_timer)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
ASSERT(info != NULL && info->itimer != NULL);
if (success) {
/* write succeeded for kernel but we're user and can have races */
struct timeval val;
DEBUG_DECLARE(bool ok;)
ASSERT(which >= 0 && which < NUM_ITIMERS);
ASSERT(cur_timer != NULL);
if (info->shared_itimer)
acquire_recursive_lock(&(*info->itimer)[which].lock);
usec_to_timeval((*info->itimer)[which].app.interval, &val);
IF_DEBUG(ok =)
safe_write_ex(&cur_timer->it_interval, sizeof(val), &val, NULL);
ASSERT(ok);
if (d_r_safe_read(&cur_timer->it_value, sizeof(val), &val)) {
/* subtract the difference between last-asked-for value
* and current value to reflect elapsed time
*/
uint64 left = (*info->itimer)[which].app.value -
((*info->itimer)[which].actual.value - timeval_to_usec(&val));
usec_to_timeval(left, &val);
IF_DEBUG(ok =)
safe_write_ex(&cur_timer->it_value, sizeof(val), &val, NULL);
ASSERT(ok);
} else
ASSERT_NOT_REACHED();
if (info->shared_itimer)
release_recursive_lock(&(*info->itimer)[which].lock);
}
}
/* handle app alarm syscall */
/* alarm uses the same itimer and could be defined in terms of setitimer */
void
handle_pre_alarm(dcontext_t *dcontext, unsigned int sec)
{
struct itimerval val;
val.it_interval.tv_usec = 0;
val.it_interval.tv_sec = 0;
val.it_value.tv_usec = 0;
val.it_value.tv_sec = sec;
handle_pre_setitimer(dcontext, ITIMER_REAL, &val, NULL);
}
void
handle_post_alarm(dcontext_t *dcontext, bool success, unsigned int sec)
{
/* alarm is always successful, so do nothing in post */
ASSERT(success);
return;
}
/***************************************************************************
* Internal DR communication
*/
typedef struct _sig_detach_info_t {
KSYNCH_TYPE *detached;
byte *sigframe_xsp;
#ifdef HAVE_SIGALTSTACK
stack_t *app_sigstack;
#endif
} sig_detach_info_t;
static void
notify_and_jmp_without_stack(KSYNCH_TYPE *notify_var, byte *continuation, byte *xsp)
{
if (ksynch_kernel_support()) {
/* Can't use dstack once we signal so in asm we do:
* futex/semaphore = 1;
* %xsp = xsp;
* dynamorio_condvar_wake_and_jmp(notify_var, continuation);
*/
#ifdef MACOS
ASSERT(sizeof(notify_var->sem) == 4);
#endif
/* We clobber xsp last just in case the compiler uses xsp to fetch the
* variables we're loading into registers.
*/
#ifdef DR_HOST_NOT_TARGET
ASSERT_NOT_REACHED();
#elif defined(X86)
# ifndef MACOS
/* i#2632: recent clang for 32-bit annoyingly won't do the right thing for
* "jmp dynamorio_condvar_wake_and_jmp" and leaves relocs so we ensure it's PIC.
* We do this first as it may end up clobbering a scratch reg like xax.
*/
void (*asm_jmp_tgt)() = dynamorio_condvar_wake_and_jmp;
asm("mov %0, %%" ASM_XDX : : "m"(asm_jmp_tgt));
# endif
asm("mov %0, %%" ASM_XAX : : "m"(notify_var));
asm("mov %0, %%" ASM_XCX : : "m"(continuation));
asm("mov %0, %%" ASM_XSP : : "m"(xsp)); /* Clobber xsp last (see above). */
# ifdef MACOS
asm("movl $1,4(%" ASM_XAX ")");
asm("jmp _dynamorio_condvar_wake_and_jmp");
# else
asm("movl $1,(%" ASM_XAX ")");
asm("jmp *%" ASM_XDX);
# endif
#elif defined(AARCHXX)
asm("ldr " ASM_R0 ", %0" : : "m"(notify_var));
asm("mov " ASM_R1 ", #1");
asm("str " ASM_R1 ",[" ASM_R0 "]");
asm("ldr " ASM_R1 ", %0" : : "m"(continuation));
asm("ldr " ASM_R2 ", %0" : : "m"(xsp));
asm("mov " ASM_XSP ", " ASM_R2); /* Clobber xsp last (see above). */
asm("b dynamorio_condvar_wake_and_jmp");
#endif
} else {
ksynch_set_value(notify_var, 1);
#ifdef DR_HOST_NOT_TARGET
ASSERT_NOT_REACHED();
#elif defined(X86)
asm("mov %0, %%" ASM_XAX : : "m"(continuation));
asm("mov %0, %%" ASM_XSP : : "m"(xsp)); /* Clobber xsp last (see above). */
asm("jmp *%" ASM_XAX);
#elif defined(AARCHXX)
asm("ldr " ASM_R0 ", %0" : : "m"(continuation));
asm("ldr " ASM_R1 ", %0" : : "m"(xsp)); /* Clobber xsp last (see above). */
asm("mov " ASM_XSP ", " ASM_R1);
asm(ASM_INDJMP " " ASM_R0);
#endif /* X86/ARM */
}
}
/* Go native from detach. This is executed on the app stack. */
static void
sig_detach_go_native(sig_detach_info_t *info)
{
byte *xsp = info->sigframe_xsp;
#ifdef HAVE_SIGALTSTACK
/* Restore the app signal stack, though sigreturn will overwrite this with the
* uc_stack in the frame's ucontext anyway (which we already set for the app).
*/
DEBUG_DECLARE(int rc =)
sigaltstack_syscall(info->app_sigstack, NULL);
ASSERT(rc == 0);
#endif
#ifdef X86
/* Skip pretcode */
xsp += sizeof(char *);
#endif
notify_and_jmp_without_stack(info->detached, (byte *)dynamorio_sigreturn, xsp);
ASSERT_NOT_REACHED();
}
/* Sets this (slave) thread to detach by directly returning from the signal. */
static void
sig_detach(dcontext_t *dcontext, sigframe_rt_t *frame, KSYNCH_TYPE *detached)
{
thread_sig_info_t *info = (thread_sig_info_t *)dcontext->signal_field;
byte *xsp;
sig_detach_info_t detach_info;
LOG(THREAD, LOG_ASYNCH, 1, "%s: detaching\n", __FUNCTION__);
if (!atomic_read_bool(&multiple_handlers_present)) {
/* Save the app's handlers (we only support one handler setups) for
* execute_native_handler().
*/
d_r_read_lock(&detached_sigact_lock);
for (int i = 0; i < SIGARRAY_SIZE; ++i) {
if (info->app_sigaction[i] != NULL &&
detached_sigact[i].handler == (handler_t)0) {
d_r_read_unlock(&detached_sigact_lock);
d_r_write_lock(&detached_sigact_lock);
if (detached_sigact[i].handler == (handler_t)0) {
memcpy(&detached_sigact[i], info->app_sigaction[i],
sizeof(detached_sigact[i]));
}
d_r_write_unlock(&detached_sigact_lock);
d_r_read_lock(&detached_sigact_lock);
}
}
d_r_read_unlock(&detached_sigact_lock);
}
/* Update the mask of the signal frame so that the later sigreturn will
* restore the app signal mask.
*/
memcpy(&frame->uc.uc_sigmask, &info->app_sigblocked, sizeof(info->app_sigblocked));
/* Copy the signal frame to the app stack.
* XXX: We live with the transparency risk of storing the signal frame on
* the app stack: we assume the app stack is writable where we need it to be,
* and that we're not clobbering any app data beyond TOS.
*/
xsp = get_sigstack_frame_ptr(dcontext, info, SUSPEND_SIGNAL, frame);
copy_frame_to_stack(dcontext, info, SUSPEND_SIGNAL, frame, xsp, false /*!pending*/);
#ifdef HAVE_SIGALTSTACK
/* Make sure the frame's sigstack reflects the app stack.
* copy_frame_to_stack() should have done this for us.
*/
ASSERT(((sigframe_rt_t *)xsp)->uc.uc_stack.ss_sp == info->app_sigstack.ss_sp);
#endif
/* Restore app segment registers. */
os_thread_not_under_dynamo(dcontext);
os_tls_thread_exit(dcontext->local_state);
#ifdef HAVE_SIGALTSTACK
/* We can't restore the app's sigstack here as that will invalidate the
* sigstack we're currently on.
*/
detach_info.app_sigstack = &info->app_sigstack;
#endif
detach_info.detached = detached;
detach_info.sigframe_xsp = xsp;
call_switch_stack(&detach_info, xsp, (void (*)(void *))sig_detach_go_native,
false /*free_initstack*/, false /*do not return*/);
ASSERT_NOT_REACHED();
}
/* Returns whether to pass on to app */
static bool
handle_suspend_signal(dcontext_t *dcontext, kernel_ucontext_t *ucxt, sigframe_rt_t *frame)
{
os_thread_data_t *ostd = (os_thread_data_t *)dcontext->os_field;
kernel_sigset_t prevmask;
sig_full_cxt_t sc_full;
ASSERT(ostd != NULL);
if (ostd->terminate) {
/* PR 297902: exit this thread, without using the dstack */
/* For MacOS, we need a stack as 32-bit syscalls take args on the stack.
* We go ahead and use it for x86 too for simpler sysenter return.
* We don't have a lot of options: we're terminating, so we go ahead
* and use the app stack.
*/
byte *app_xsp;
if (IS_CLIENT_THREAD(dcontext))
app_xsp = (byte *)SIGCXT_FROM_UCXT(ucxt)->SC_XSP;
else
app_xsp = (byte *)get_mcontext(dcontext)->xsp;
LOG(THREAD, LOG_ASYNCH, 2, "handle_suspend_signal: exiting\n");
ASSERT(app_xsp != NULL);
notify_and_jmp_without_stack(&ostd->terminated, (byte *)dynamorio_sys_exit,
app_xsp);
ASSERT_NOT_REACHED();
return false;
}
if (!doing_detach && is_thread_currently_native(dcontext->thread_record) &&
!IS_CLIENT_THREAD(dcontext) IF_APP_EXPORTS(&&!dr_api_exit)) {
if (!sig_take_over(ucxt))
return false;
ASSERT_NOT_REACHED(); /* else, shouldn't return */
}
/* If suspend_count is 0, we are not trying to suspend this thread
* (os_thread_resume() may have already decremented suspend_count to 0, but
* os_thread_suspend() will not send a signal until this thread unsets
* ostd->suspended, so not having a lock around the suspend_count read is
* ok), so pass signal to app.
* If we are trying or have already suspended this thread, our own
* os_thread_suspend() will not send a 2nd suspend signal until we are
* completely resumed, so we can distinguish app uses of SUSPEND_SIGNAL. We
* can't have a race between the read and write of suspended_sigcxt b/c
* signals are blocked. It's fine to have a race and reorder the app's
* signal w/ DR's.
*/
if (ostd->suspend_count == 0)
return true; /* pass to app */
ASSERT(ostd->suspended_sigcxt == NULL);
/* XXX: we're not setting DR_WHERE_SIGNAL_HANDLER in enough places.
* It's trickier than other whereamis b/c we want to resume the
* prior whereami when we return from the handler, but there are
* complex control paths that do not always return.
* We try to at least do it for the ksynch_wait here.
*/
dr_where_am_i_t prior_whereami = dcontext->whereami;
dcontext->whereami = DR_WHERE_SIGNAL_HANDLER;
sig_full_initialize(&sc_full, ucxt);
ostd->suspended_sigcxt = &sc_full;
LOG(THREAD, LOG_ASYNCH, 2, "handle_suspend_signal: suspended now\n");
/* We cannot use mutexes here as we have interrupted DR at an
* arbitrary point! Thus we can't use the event_t routines.
* However, the existing synch and check above prevent any
* re-entrance here, and our cond vars target just a single thread,
* so we can get away w/o a mutex.
*/
/* Notify os_thread_suspend that it can now return, as this thread is
* officially suspended now and is ready for thread_{get,set}_mcontext.
*/
ASSERT(ksynch_get_value(&ostd->suspended) == 0);
ksynch_set_value(&ostd->suspended, 1);
ksynch_wake_all(&ostd->suspended);
/* We're sitting on our sigaltstack w/ all signals blocked. We're
* going to stay here but unblock all signals so we don't lose any
* delivered while we're waiting. We're at a safe enough point (now
* that we've set ostd->suspended: i#5779) to re-enter
* master_signal_handler(). We use a mutex in thread_{suspend,resume} to
* prevent our own re-suspension signal from arriving before we've
* re-blocked on the resume.
*/
sigprocmask_syscall(SIG_SETMASK, SIGMASK_FROM_UCXT(ucxt), &prevmask,
sizeof(ucxt->uc_sigmask));
/* i#96/PR 295561: use futex(2) if available */
while (ksynch_get_value(&ostd->wakeup) == 0) {
/* Waits only if the wakeup flag is not set as 1. Return value
* doesn't matter because the flag will be re-checked.
*/
ksynch_wait(&ostd->wakeup, 0, 0);
if (ksynch_get_value(&ostd->wakeup) == 0) {
/* If it still has to wait, give up the cpu. */
os_thread_yield();
}
}
LOG(THREAD, LOG_ASYNCH, 2, "handle_suspend_signal: awake now\n");
/* re-block so our exit from master_signal_handler is not interrupted */
sigprocmask_syscall(SIG_SETMASK, &prevmask, NULL, sizeof(prevmask));
ostd->suspended_sigcxt = NULL;
/* Notify os_thread_resume that it can return now, which (assuming
* suspend_count is back to 0) means it's then safe to re-suspend.
*/
ksynch_set_value(&ostd->suspended, 0); /*reset prior to signalling os_thread_resume*/
ksynch_set_value(&ostd->resumed, 1);
ksynch_wake_all(&ostd->resumed);
dcontext->whereami = prior_whereami;
if (ostd->retakeover) {
ostd->retakeover = false;
sig_take_over(ucxt); /* shouldn't return for this case */
ASSERT_NOT_REACHED();
} else if (ostd->do_detach) {
ostd->do_detach = false;
sig_detach(dcontext, frame, &ostd->detached); /* no return */
ASSERT_NOT_REACHED();
}
return false; /* do not pass to app */
}
/* PR 206278: for try/except we need to save the signal mask */
void
dr_setjmp_sigmask(dr_jmp_buf_t *buf)
{
/* i#226/PR 492568: we rely on the kernel storing the prior mask in the
* signal frame, so we do not need to store it on every setjmp, which
* can be a performance hit.
*/
#ifdef DEBUG
sigprocmask_syscall(SIG_SETMASK, NULL, &buf->sigmask, sizeof(buf->sigmask));
#endif
}
/* i#61/PR 211530: nudge on Linux.
* Determines whether this is a nudge signal, and if so queues up a nudge,
* or is an app signal. Returns whether to pass the signal on to the app.
*/
static bool
handle_nudge_signal(dcontext_t *dcontext, kernel_siginfo_t *siginfo,
kernel_ucontext_t *ucxt)
{
sigcontext_t *sc = SIGCXT_FROM_UCXT(ucxt);
nudge_arg_t *arg = (nudge_arg_t *)siginfo;
instr_t instr;
char buf[MAX_INSTR_LENGTH];
/* Distinguish a nudge from an app signal. An app using libc sigqueue()
* will never have its signal mistaken as libc does not expose the kernel_siginfo_t
* and always passes 0 for si_errno, so we're only worried beyond our
* si_code check about an app using a raw syscall that is deliberately
* trying to fool us.
* While there is a lot of padding space in kernel_siginfo_t, the kernel doesn't
* copy it through on SYS_rt_sigqueueinfo so we don't have room for any
* dedicated magic numbers. The client id could function as a magic
* number for client nudges, but I don't think we want to kill the app
* if an external nudger types the client id wrong.
*/
LOG(THREAD, LOG_ASYNCH, 2, "%s: sig=%d code=%d errno=%d\n", __FUNCTION__,
siginfo->si_signo, siginfo->si_code, siginfo->si_errno);
if (siginfo->si_signo !=
NUDGESIG_SIGNUM
/* PR 477454: remove the IF_NOT_VMX86 once we have nudge-arg support */
IF_NOT_VMX86(|| siginfo->si_code != SI_QUEUE || siginfo->si_errno == 0)) {
return true; /* pass to app */
}
#if defined(CLIENT_INTERFACE) && !defined(VMX86_SERVER)
DODEBUG({
if (TEST(NUDGE_GENERIC(client), arg->nudge_action_mask) &&
!is_valid_client_id(arg->client_id)) {
SYSLOG_INTERNAL_WARNING("received client nudge for invalid id=0x%x",
arg->client_id);
}
});
#endif
if (dynamo_exited || !dynamo_initialized || dcontext == NULL) {
/* Ignore the nudge: too early, or too late.
* Xref Windows handling of such cases in nudge.c: old case 5702, etc.
* We do this before the illegal-instr check b/c it's unsafe to decode
* if too early or too late.
*/
SYSLOG_INTERNAL_WARNING("too-early or too-late nudge: ignoring");
return false; /* do not pass to app */
}
/* As a further check, try to detect whether this was raised synchronously
* from a real illegal instr: though si_code for that should not be
* SI_QUEUE. It's possible a nudge happened to come at a bad instr before
* it faulted, or maybe the instr after a syscall or other wait spot is
* illegal, but we'll live with that risk.
*/
ASSERT(NUDGESIG_SIGNUM == SIGILL); /* else this check makes no sense */
instr_init(dcontext, &instr);
if (d_r_safe_read((byte *)sc->SC_XIP, sizeof(buf), buf) &&
(decode(dcontext, (byte *)buf, &instr) == NULL ||
/* check for ud2 (xref PR 523161) */
instr_is_undefined(&instr))) {
LOG(THREAD, LOG_ASYNCH, 2, "%s: real illegal instr @" PFX "\n", __FUNCTION__,
sc->SC_XIP);
DOLOG(2, LOG_ASYNCH,
{ disassemble_with_bytes(dcontext, (byte *)sc->SC_XIP, THREAD); });
instr_free(dcontext, &instr);
return true; /* pass to app */
}
instr_free(dcontext, &instr);
#ifdef VMX86_SERVER
/* Treat as a client nudge until we have PR 477454 */
if (siginfo->si_errno == 0) {
arg->version = NUDGE_ARG_CURRENT_VERSION;
arg->flags = 0;
arg->nudge_action_mask = NUDGE_GENERIC(client);
arg->client_id = 0;
arg->client_arg = 0;
}
#endif
LOG(THREAD, LOG_ASYNCH, 1,
"received nudge version=%u flags=0x%x mask=0x%x id=0x%08x "
"arg=0x" ZHEX64_FORMAT_STRING "\n",
arg->version, arg->flags, arg->nudge_action_mask, arg->client_id,
arg->client_arg);
SYSLOG_INTERNAL_INFO("received nudge mask=0x%x id=0x%08x arg=0x" ZHEX64_FORMAT_STRING,
arg->nudge_action_mask, arg->client_id, arg->client_arg);
/* We need to handle the nudge at a safe, nolinking spot */
if (safe_is_in_fcache(dcontext, (byte *)sc->SC_XIP, (byte *)sc->SC_XSP) &&
dcontext->interrupted_for_nudge == NULL) {
/* We unlink the interrupted fragment and skip any inlined syscalls to
* bound the nudge delivery time. If we already unlinked one we assume
* that's sufficient.
*/
fragment_t wrapper;
fragment_t *f = fragment_pclookup(dcontext, (byte *)sc->SC_XIP, &wrapper);
if (f != NULL) {
if (unlink_fragment_for_signal(dcontext, f, (byte *)sc->SC_XIP))
dcontext->interrupted_for_nudge = f;
}
}
/* No lock is needed since thread-private and this signal is blocked now */
nudge_add_pending(dcontext, arg);
return false; /* do not pass to app */
}