summaryrefslogtreecommitdiff
path: root/src/pal/src/exception/signal.cpp
diff options
context:
space:
mode:
authorJiyoung Yun <jy910.yun@samsung.com>2016-11-23 19:09:09 +0900
committerJiyoung Yun <jy910.yun@samsung.com>2016-11-23 19:09:09 +0900
commit4b4aad7217d3292650e77eec2cf4c198ea9c3b4b (patch)
tree98110734c91668dfdbb126fcc0e15ddbd93738ca /src/pal/src/exception/signal.cpp
parentfa45f57ed55137c75ac870356a1b8f76c84b229c (diff)
downloadcoreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.gz
coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.bz2
coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.zip
Imported Upstream version 1.1.0upstream/1.1.0
Diffstat (limited to 'src/pal/src/exception/signal.cpp')
-rw-r--r--src/pal/src/exception/signal.cpp705
1 files changed, 705 insertions, 0 deletions
diff --git a/src/pal/src/exception/signal.cpp b/src/pal/src/exception/signal.cpp
new file mode 100644
index 0000000000..c2c217993a
--- /dev/null
+++ b/src/pal/src/exception/signal.cpp
@@ -0,0 +1,705 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*++
+
+
+
+Module Name:
+
+ exception/signal.cpp
+
+Abstract:
+
+ Signal handler implementation (map signals to exceptions)
+
+
+
+--*/
+
+#include "pal/dbgmsg.h"
+SET_DEFAULT_DEBUG_CHANNEL(EXCEPT); // some headers have code with asserts, so do this first
+
+#include "pal/corunix.hpp"
+#include "pal/handleapi.hpp"
+#include "pal/thread.hpp"
+#include "pal/threadinfo.hpp"
+#include "pal/threadsusp.hpp"
+#include "pal/seh.hpp"
+
+#include "pal/palinternal.h"
+#if !HAVE_MACH_EXCEPTIONS
+#include "pal/init.h"
+#include "pal/process.h"
+#include "pal/debug.h"
+
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/ucontext.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+#include "pal/context.h"
+
+using namespace CorUnix;
+
+#ifdef SIGRTMIN
+#define INJECT_ACTIVATION_SIGNAL SIGRTMIN
+#endif
+
+#if !defined(INJECT_ACTIVATION_SIGNAL) && defined(FEATURE_HIJACK)
+#error FEATURE_HIJACK requires INJECT_ACTIVATION_SIGNAL to be defined
+#endif
+
+/* local type definitions *****************************************************/
+
+#if !HAVE_SIGINFO_T
+/* This allows us to compile on platforms that don't have siginfo_t.
+ * Exceptions will work poorly on those platforms. */
+#warning Exceptions will work poorly on this platform
+typedef void *siginfo_t;
+#endif /* !HAVE_SIGINFO_T */
+typedef void (*SIGFUNC)(int, siginfo_t *, void *);
+
+/* internal function declarations *********************************************/
+
+static void sigill_handler(int code, siginfo_t *siginfo, void *context);
+static void sigfpe_handler(int code, siginfo_t *siginfo, void *context);
+static void sigsegv_handler(int code, siginfo_t *siginfo, void *context);
+static void sigtrap_handler(int code, siginfo_t *siginfo, void *context);
+static void sigbus_handler(int code, siginfo_t *siginfo, void *context);
+static void sigint_handler(int code, siginfo_t *siginfo, void *context);
+static void sigquit_handler(int code, siginfo_t *siginfo, void *context);
+static void sigterm_handler(int code, siginfo_t *siginfo, void *context);
+
+static bool common_signal_handler(int code, siginfo_t *siginfo, void *sigcontext, int numParams, ...);
+
+#ifdef INJECT_ACTIVATION_SIGNAL
+static void inject_activation_handler(int code, siginfo_t *siginfo, void *context);
+#endif
+
+static void handle_signal(int signal_id, SIGFUNC sigfunc, struct sigaction *previousAction);
+static void restore_signal(int signal_id, struct sigaction *previousAction);
+
+/* internal data declarations *********************************************/
+
+struct sigaction g_previous_sigill;
+struct sigaction g_previous_sigtrap;
+struct sigaction g_previous_sigfpe;
+struct sigaction g_previous_sigbus;
+struct sigaction g_previous_sigsegv;
+struct sigaction g_previous_sigint;
+struct sigaction g_previous_sigquit;
+struct sigaction g_previous_sigterm;
+
+static bool registered_sigterm_handler = false;
+
+#ifdef INJECT_ACTIVATION_SIGNAL
+struct sigaction g_previous_activation;
+#endif
+
+/* public function definitions ************************************************/
+
+/*++
+Function :
+ SEHInitializeSignals
+
+ Set up signal handlers to catch signals and translate them to exceptions
+
+Parameters :
+ None
+
+Return :
+ TRUE in case of a success, FALSE otherwise
+--*/
+BOOL SEHInitializeSignals(DWORD flags)
+{
+ TRACE("Initializing signal handlers\n");
+
+ /* we call handle_signal for every possible signal, even
+ if we don't provide a signal handler.
+
+ handle_signal will set SA_RESTART flag for specified signal.
+ Therefore, all signals will have SA_RESTART flag set, preventing
+ slow Unix system calls from being interrupted. On systems without
+ siginfo_t, SIGKILL and SIGSTOP can't be restarted, so we don't
+ handle those signals. Both the Darwin and FreeBSD man pages say
+ that SIGKILL and SIGSTOP can't be handled, but FreeBSD allows us
+ to register a handler for them anyway. We don't do that.
+
+ see sigaction man page for more details
+ */
+ handle_signal(SIGILL, sigill_handler, &g_previous_sigill);
+ handle_signal(SIGTRAP, sigtrap_handler, &g_previous_sigtrap);
+ handle_signal(SIGFPE, sigfpe_handler, &g_previous_sigfpe);
+ handle_signal(SIGBUS, sigbus_handler, &g_previous_sigbus);
+ handle_signal(SIGSEGV, sigsegv_handler, &g_previous_sigsegv);
+ handle_signal(SIGINT, sigint_handler, &g_previous_sigint);
+ handle_signal(SIGQUIT, sigquit_handler, &g_previous_sigquit);
+
+ if (flags & PAL_INITIALIZE_REGISTER_SIGTERM_HANDLER)
+ {
+ handle_signal(SIGTERM, sigterm_handler, &g_previous_sigterm);
+ registered_sigterm_handler = true;
+ }
+
+#ifdef INJECT_ACTIVATION_SIGNAL
+ handle_signal(INJECT_ACTIVATION_SIGNAL, inject_activation_handler, &g_previous_activation);
+#endif
+
+ /* The default action for SIGPIPE is process termination.
+ Since SIGPIPE can be signaled when trying to write on a socket for which
+ the connection has been dropped, we need to tell the system we want
+ to ignore this signal.
+
+ Instead of terminating the process, the system call which would had
+ issued a SIGPIPE will, instead, report an error and set errno to EPIPE.
+ */
+ signal(SIGPIPE, SIG_IGN);
+
+ return TRUE;
+}
+
+/*++
+Function :
+ SEHCleanupSignals
+
+ Restore default signal handlers
+
+Parameters :
+ None
+
+ (no return value)
+
+note :
+reason for this function is that during PAL_Terminate, we reach a point where
+SEH isn't possible anymore (handle manager is off, etc). Past that point,
+we can't avoid crashing on a signal.
+--*/
+void SEHCleanupSignals()
+{
+ TRACE("Restoring default signal handlers\n");
+
+ restore_signal(SIGILL, &g_previous_sigill);
+ restore_signal(SIGTRAP, &g_previous_sigtrap);
+ restore_signal(SIGFPE, &g_previous_sigfpe);
+ restore_signal(SIGBUS, &g_previous_sigbus);
+ restore_signal(SIGSEGV, &g_previous_sigsegv);
+ restore_signal(SIGINT, &g_previous_sigint);
+ restore_signal(SIGQUIT, &g_previous_sigquit);
+
+ if (registered_sigterm_handler)
+ {
+ restore_signal(SIGTERM, &g_previous_sigterm);
+ }
+
+#ifdef INJECT_ACTIVATION_SIGNAL
+ restore_signal(INJECT_ACTIVATION_SIGNAL, &g_previous_activation);
+#endif
+}
+
+/* internal function definitions **********************************************/
+
+/*++
+Function :
+ sigill_handler
+
+ handle SIGILL signal (EXCEPTION_ILLEGAL_INSTRUCTION, others?)
+
+Parameters :
+ POSIX signal handler parameter list ("man sigaction" for details)
+
+ (no return value)
+--*/
+static void sigill_handler(int code, siginfo_t *siginfo, void *context)
+{
+ if (PALIsInitialized())
+ {
+ if (common_signal_handler(code, siginfo, context, 0))
+ {
+ return;
+ }
+ }
+
+ if (g_previous_sigill.sa_sigaction != NULL)
+ {
+ g_previous_sigill.sa_sigaction(code, siginfo, context);
+ }
+ else
+ {
+ // Restore the original or default handler and restart h/w exception
+ restore_signal(code, &g_previous_sigill);
+ }
+
+ PROCNotifyProcessShutdown();
+}
+
+/*++
+Function :
+ sigfpe_handler
+
+ handle SIGFPE signal (division by zero, floating point exception)
+
+Parameters :
+ POSIX signal handler parameter list ("man sigaction" for details)
+
+ (no return value)
+--*/
+static void sigfpe_handler(int code, siginfo_t *siginfo, void *context)
+{
+ if (PALIsInitialized())
+ {
+ if (common_signal_handler(code, siginfo, context, 0))
+ {
+ return;
+ }
+ }
+
+ if (g_previous_sigfpe.sa_sigaction != NULL)
+ {
+ g_previous_sigfpe.sa_sigaction(code, siginfo, context);
+ }
+ else
+ {
+ // Restore the original or default handler and restart h/w exception
+ restore_signal(code, &g_previous_sigfpe);
+ }
+
+ PROCNotifyProcessShutdown();
+}
+
+/*++
+Function :
+ sigsegv_handler
+
+ handle SIGSEGV signal (EXCEPTION_ACCESS_VIOLATION, others)
+
+Parameters :
+ POSIX signal handler parameter list ("man sigaction" for details)
+
+ (no return value)
+--*/
+static void sigsegv_handler(int code, siginfo_t *siginfo, void *context)
+{
+ if (PALIsInitialized())
+ {
+ // TODO: First variable parameter says whether a read (0) or write (non-0) caused the
+ // fault. We must disassemble the instruction at record.ExceptionAddress
+ // to correctly fill in this value.
+ if (common_signal_handler(code, siginfo, context, 2, (size_t)0, (size_t)siginfo->si_addr))
+ {
+ return;
+ }
+ }
+
+ if (g_previous_sigsegv.sa_sigaction != NULL)
+ {
+ g_previous_sigsegv.sa_sigaction(code, siginfo, context);
+ }
+ else
+ {
+ // Restore the original or default handler and restart h/w exception
+ restore_signal(code, &g_previous_sigsegv);
+ }
+
+ PROCNotifyProcessShutdown();
+}
+
+/*++
+Function :
+ sigtrap_handler
+
+ handle SIGTRAP signal (EXCEPTION_SINGLE_STEP, EXCEPTION_BREAKPOINT)
+
+Parameters :
+ POSIX signal handler parameter list ("man sigaction" for details)
+
+ (no return value)
+--*/
+static void sigtrap_handler(int code, siginfo_t *siginfo, void *context)
+{
+ if (PALIsInitialized())
+ {
+ if (common_signal_handler(code, siginfo, context, 0))
+ {
+ return;
+ }
+ }
+
+ if (g_previous_sigtrap.sa_sigaction != NULL)
+ {
+ g_previous_sigtrap.sa_sigaction(code, siginfo, context);
+ }
+ else
+ {
+ // We abort instead of restore the original or default handler and returning
+ // because returning from a SIGTRAP handler continues execution past the trap.
+ PROCAbort();
+ }
+
+ PROCNotifyProcessShutdown();
+}
+
+/*++
+Function :
+ sigbus_handler
+
+ handle SIGBUS signal (EXCEPTION_ACCESS_VIOLATION?)
+
+Parameters :
+ POSIX signal handler parameter list ("man sigaction" for details)
+
+ (no return value)
+--*/
+static void sigbus_handler(int code, siginfo_t *siginfo, void *context)
+{
+ if (PALIsInitialized())
+ {
+ // TODO: First variable parameter says whether a read (0) or write (non-0) caused the
+ // fault. We must disassemble the instruction at record.ExceptionAddress
+ // to correctly fill in this value.
+ if (common_signal_handler(code, siginfo, context, 2, (size_t)0, (size_t)siginfo->si_addr))
+ {
+ return;
+ }
+ }
+
+ if (g_previous_sigbus.sa_sigaction != NULL)
+ {
+ g_previous_sigbus.sa_sigaction(code, siginfo, context);
+ }
+ else
+ {
+ // Restore the original or default handler and restart h/w exception
+ restore_signal(code, &g_previous_sigbus);
+ }
+
+ PROCNotifyProcessShutdown();
+}
+
+/*++
+Function :
+ sigint_handler
+
+ handle SIGINT signal
+
+Parameters :
+ POSIX signal handler parameter list ("man sigaction" for details)
+
+ (no return value)
+--*/
+static void sigint_handler(int code, siginfo_t *siginfo, void *context)
+{
+ PROCNotifyProcessShutdown();
+
+ // Restore the original or default handler and resend signal
+ restore_signal(code, &g_previous_sigint);
+ kill(gPID, code);
+}
+
+/*++
+Function :
+ sigquit_handler
+
+ handle SIGQUIT signal
+
+Parameters :
+ POSIX signal handler parameter list ("man sigaction" for details)
+
+ (no return value)
+--*/
+static void sigquit_handler(int code, siginfo_t *siginfo, void *context)
+{
+ PROCNotifyProcessShutdown();
+
+ // Restore the original or default handler and resend signal
+ restore_signal(code, &g_previous_sigquit);
+ kill(gPID, code);
+}
+
+/*++
+Function :
+ sigterm_handler
+
+ handle SIGTERM signal
+
+Parameters :
+ POSIX signal handler parameter list ("man sigaction" for details)
+
+ (no return value)
+--*/
+static void sigterm_handler(int code, siginfo_t *siginfo, void *context)
+{
+ if (PALIsInitialized())
+ {
+ // g_pSynchronizationManager shouldn't be null if PAL is initialized.
+ _ASSERTE(g_pSynchronizationManager != nullptr);
+
+ g_pSynchronizationManager->SendTerminationRequestToWorkerThread();
+ }
+ else
+ {
+ if (g_previous_sigterm.sa_sigaction != NULL)
+ {
+ g_previous_sigterm.sa_sigaction(code, siginfo, context);
+ }
+ }
+}
+
+#ifdef INJECT_ACTIVATION_SIGNAL
+/*++
+Function :
+ inject_activation_handler
+
+ Handle the INJECT_ACTIVATION_SIGNAL signal. This signal interrupts a running thread
+ so it can call the activation function that was specified when sending the signal.
+
+Parameters :
+ POSIX signal handler parameter list ("man sigaction" for details)
+
+(no return value)
+--*/
+static void inject_activation_handler(int code, siginfo_t *siginfo, void *context)
+{
+ // Only accept activations from the current process
+ if (g_activationFunction != NULL && siginfo->si_pid == getpid())
+ {
+ _ASSERTE(g_safeActivationCheckFunction != NULL);
+
+ native_context_t *ucontext = (native_context_t *)context;
+
+ CONTEXT winContext;
+ CONTEXTFromNativeContext(
+ ucontext,
+ &winContext,
+ CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT);
+
+ if (g_safeActivationCheckFunction(CONTEXTGetPC(&winContext), /* checkingCurrentThread */ TRUE))
+ {
+ g_activationFunction(&winContext);
+ // Activation function may have modified the context, so update it.
+ CONTEXTToNativeContext(&winContext, ucontext);
+ }
+ }
+ else if (g_previous_activation.sa_sigaction != NULL)
+ {
+ g_previous_activation.sa_sigaction(code, siginfo, context);
+ }
+}
+#endif
+
+/*++
+Function :
+ InjectActivationInternal
+
+ Interrupt the specified thread and have it call the activationFunction passed in
+
+Parameters :
+ pThread - target PAL thread
+ activationFunction - function to call
+
+(no return value)
+--*/
+PAL_ERROR InjectActivationInternal(CorUnix::CPalThread* pThread)
+{
+#ifdef INJECT_ACTIVATION_SIGNAL
+ int status = pthread_kill(pThread->GetPThreadSelf(), INJECT_ACTIVATION_SIGNAL);
+ if (status != 0)
+ {
+ // Failure to send the signal is fatal. There are only two cases when sending
+ // the signal can fail. First, if the signal ID is invalid and second,
+ // if the thread doesn't exist anymore.
+ PROCAbort();
+ }
+
+ return NO_ERROR;
+#else
+ return ERROR_CANCELLED;
+#endif
+}
+
+/*++
+Function :
+ SEHSetSafeState
+
+ specify whether the current thread is in a state where exception handling
+ of signals can be done safely
+
+Parameters:
+ BOOL state : TRUE if the thread is safe, FALSE otherwise
+
+(no return value)
+--*/
+void SEHSetSafeState(CPalThread *pthrCurrent, BOOL state)
+{
+ if (NULL == pthrCurrent)
+ {
+ ASSERT( "Unable to get the thread object.\n" );
+ return;
+ }
+ pthrCurrent->sehInfo.safe_state = state;
+}
+
+/*++
+Function :
+ SEHGetSafeState
+
+ determine whether the current thread is in a state where exception handling
+ of signals can be done safely
+
+ (no parameters)
+
+Return value :
+ TRUE if the thread is in a safe state, FALSE otherwise
+--*/
+BOOL SEHGetSafeState(CPalThread *pthrCurrent)
+{
+ if (NULL == pthrCurrent)
+ {
+ ASSERT( "Unable to get the thread object.\n" );
+ return FALSE;
+ }
+ return pthrCurrent->sehInfo.safe_state;
+}
+
+/*++
+Function :
+ common_signal_handler
+
+ common code for all signal handlers
+
+Parameters :
+ int code : signal received
+ siginfo_t *siginfo : siginfo passed to the signal handler
+ void *context : context structure passed to the signal handler
+ int numParams : number of variable parameters of the exception
+ ... : variable parameters of the exception (each of size_t type)
+
+ Returns true if the execution should continue or false if the exception was unhandled
+Note:
+ the "pointers" parameter should contain a valid exception record pointer,
+ but the ContextRecord pointer will be overwritten.
+--*/
+static bool common_signal_handler(int code, siginfo_t *siginfo, void *sigcontext, int numParams, ...)
+{
+ sigset_t signal_set;
+ CONTEXT *contextRecord;
+ EXCEPTION_RECORD *exceptionRecord;
+ native_context_t *ucontext;
+
+ ucontext = (native_context_t *)sigcontext;
+
+ AllocateExceptionRecords(&exceptionRecord, &contextRecord);
+
+ exceptionRecord->ExceptionCode = CONTEXTGetExceptionCodeForSignal(siginfo, ucontext);
+ exceptionRecord->ExceptionFlags = EXCEPTION_IS_SIGNAL;
+ exceptionRecord->ExceptionRecord = NULL;
+ exceptionRecord->ExceptionAddress = GetNativeContextPC(ucontext);
+ exceptionRecord->NumberParameters = numParams;
+
+ va_list params;
+ va_start(params, numParams);
+
+ for (int i = 0; i < numParams; i++)
+ {
+ exceptionRecord->ExceptionInformation[i] = va_arg(params, size_t);
+ }
+
+ // Pre-populate context with data from current frame, because ucontext doesn't have some data (e.g. SS register)
+ // which is required for restoring context
+ RtlCaptureContext(contextRecord);
+
+ ULONG contextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT;
+
+#if defined(_AMD64_)
+ contextFlags |= CONTEXT_XSTATE;
+#endif
+
+ // Fill context record with required information. from pal.h:
+ // On non-Win32 platforms, the CONTEXT pointer in the
+ // PEXCEPTION_POINTERS will contain at least the CONTEXT_CONTROL registers.
+ CONTEXTFromNativeContext(ucontext, contextRecord, contextFlags);
+
+ /* Unmask signal so we can receive it again */
+ sigemptyset(&signal_set);
+ sigaddset(&signal_set, code);
+ int sigmaskRet = pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
+ if (sigmaskRet != 0)
+ {
+ ASSERT("pthread_sigmask failed; error number is %d\n", sigmaskRet);
+ }
+
+ contextRecord->ContextFlags |= CONTEXT_EXCEPTION_ACTIVE;
+ // The exception object takes ownership of the exceptionRecord and contextRecord
+ PAL_SEHException exception(exceptionRecord, contextRecord);
+
+ if (SEHProcessException(&exception))
+ {
+ // Exception handling may have modified the context, so update it.
+ CONTEXTToNativeContext(contextRecord, ucontext);
+ return true;
+ }
+
+ return false;
+}
+
+/*++
+Function :
+ handle_signal
+
+ register handler for specified signal
+
+Parameters :
+ int signal_id : signal to handle
+ SIGFUNC sigfunc : signal handler
+ previousAction : previous sigaction struct
+
+ (no return value)
+
+note : if sigfunc is NULL, the default signal handler is restored
+--*/
+void handle_signal(int signal_id, SIGFUNC sigfunc, struct sigaction *previousAction)
+{
+ struct sigaction newAction;
+
+ newAction.sa_flags = SA_RESTART;
+#if HAVE_SIGINFO_T
+ newAction.sa_handler = NULL;
+ newAction.sa_sigaction = sigfunc;
+ newAction.sa_flags |= SA_SIGINFO;
+#else /* HAVE_SIGINFO_T */
+ newAction.sa_handler = SIG_DFL;
+#endif /* HAVE_SIGINFO_T */
+ sigemptyset(&newAction.sa_mask);
+
+ if (-1 == sigaction(signal_id, &newAction, previousAction))
+ {
+ ASSERT("handle_signal: sigaction() call failed with error code %d (%s)\n",
+ errno, strerror(errno));
+ }
+}
+
+/*++
+Function :
+ restore_signal
+
+ restore handler for specified signal
+
+Parameters :
+ int signal_id : signal to handle
+ previousAction : previous sigaction struct to restore
+
+ (no return value)
+--*/
+void restore_signal(int signal_id, struct sigaction *previousAction)
+{
+ if (-1 == sigaction(signal_id, previousAction, NULL))
+ {
+ ASSERT("restore_signal: sigaction() call failed with error code %d (%s)\n",
+ errno, strerror(errno));
+ }
+}
+
+#endif // !HAVE_MACH_EXCEPTIONS