From ce090df692667a42de5c8eb2e96e922956f464ba Mon Sep 17 00:00:00 2001 From: Aditya Mandaleeka Date: Mon, 4 Apr 2016 16:11:02 -0700 Subject: [PATCH] Add SIGTERM handling logic that properly shuts down the EE. --- src/pal/inc/pal.h | 10 +- src/pal/src/exception/seh.cpp | 54 ++++++++- src/pal/src/exception/signal.cpp | 176 ++++++++++++++++++++++++++++++ src/pal/src/include/pal/process.h | 2 +- src/pal/src/include/pal/seh.hpp | 12 ++ src/pal/src/thread/process.cpp | 2 +- src/vm/exceptionhandling.cpp | 8 ++ 7 files changed, 259 insertions(+), 5 deletions(-) diff --git a/src/pal/inc/pal.h b/src/pal/inc/pal.h index 590cb64b1aef..ee3ecbec5299 100644 --- a/src/pal/inc/pal.h +++ b/src/pal/inc/pal.h @@ -480,9 +480,10 @@ typedef long time_t; #define PAL_INITIALIZE_SYNC_THREAD 0x01 #define PAL_INITIALIZE_EXEC_ALLOCATOR 0x02 #define PAL_INITIALIZE_STD_HANDLES 0x04 +#define PAL_INITIALIZE_SIGNAL_THREAD 0x08 // PAL_Initialize() flags -#define PAL_INITIALIZE (PAL_INITIALIZE_SYNC_THREAD | PAL_INITIALIZE_STD_HANDLES) +#define PAL_INITIALIZE (PAL_INITIALIZE_SYNC_THREAD | PAL_INITIALIZE_STD_HANDLES | PAL_INITIALIZE_SIGNAL_THREAD) // PAL_InitializeDLL() flags - don't start any of the helper threads #define PAL_INITIALIZE_DLL PAL_INITIALIZE_NONE @@ -6487,6 +6488,7 @@ struct PAL_SEHException }; typedef VOID (PALAPI *PHARDWARE_EXCEPTION_HANDLER)(PAL_SEHException* ex); +typedef VOID (PALAPI *PTERMINATION_REQUEST_HANDLER)(); typedef DWORD (PALAPI *PGET_GCMARKER_EXCEPTION_CODE)(LPVOID ip); PALIMPORT @@ -6508,6 +6510,12 @@ PAL_ThrowExceptionFromContext( IN CONTEXT* context, IN PAL_SEHException* ex); +PALIMPORT +VOID +PALAPI +PAL_SetTerminationRequestHandler( + IN PTERMINATION_REQUEST_HANDLER terminationRequestHandler); + // // This holder is used to indicate that a hardware // exception should be raised as a C++ exception diff --git a/src/pal/src/exception/seh.cpp b/src/pal/src/exception/seh.cpp index 4cc30c23e008..30b588deda85 100644 --- a/src/pal/src/exception/seh.cpp +++ b/src/pal/src/exception/seh.cpp @@ -51,8 +51,18 @@ const UINT RESERVED_SEH_BIT = 0x800000; /* Internal variables definitions **********************************************/ -PHARDWARE_EXCEPTION_HANDLER g_hardwareExceptionHandler = NULL; -PGET_GCMARKER_EXCEPTION_CODE g_getGcMarkerExceptionCode = NULL; +PHARDWARE_EXCEPTION_HANDLER g_hardwareExceptionHandler = nullptr; +PGET_GCMARKER_EXCEPTION_CODE g_getGcMarkerExceptionCode = nullptr; +PTERMINATION_REQUEST_HANDLER g_terminationRequestHandler = nullptr; + +/* Internal function declarations *********************************************/ + +#if !HAVE_MACH_EXCEPTIONS +PAL_ERROR +StartExternalSignalHandlerThread( + CPalThread *pthr); +#endif // !HAVE_MACH_EXCEPTIONS + /* Internal function definitions **********************************************/ @@ -80,6 +90,17 @@ SEHInitialize (CPalThread *pthrCurrent, DWORD flags) SEHCleanup(); return FALSE; } + + if (flags & PAL_INITIALIZE_SIGNAL_THREAD) + { + PAL_ERROR palError = StartExternalSignalHandlerThread(pthrCurrent); + if (palError != NO_ERROR) + { + ERROR("StartExternalSignalHandlerThread returned %d\n", palError); + SEHCleanup(); + return FALSE; + } + } #endif return TRUE; @@ -129,6 +150,26 @@ PAL_SetHardwareExceptionHandler( g_hardwareExceptionHandler = exceptionHandler; } +/*++ +Function: + PAL_SetTerminationRequestHandler + + Register a termination request handler. + +Parameters: + terminationHandler - handler for termination request + +Return value: + None +--*/ +VOID +PALAPI +PAL_SetTerminationRequestHandler( + IN PTERMINATION_REQUEST_HANDLER terminationHandler) +{ + g_terminationRequestHandler = terminationHandler; +} + /*++ Function: PAL_SetGetGcMarkerExceptionCode @@ -225,6 +266,15 @@ SEHProcessException(PEXCEPTION_POINTERS pointers) pointers->ExceptionRecord->ExceptionCode, pointers->ExceptionRecord->ExceptionAddress); } +VOID +SEHHandleTerminationRequest() +{ + if (g_terminationRequestHandler != NULL) + { + g_terminationRequestHandler(); + } +} + /*++ Function : SEHEnable diff --git a/src/pal/src/exception/signal.cpp b/src/pal/src/exception/signal.cpp index 25136a703b32..0e3ef25acb6b 100644 --- a/src/pal/src/exception/signal.cpp +++ b/src/pal/src/exception/signal.cpp @@ -72,6 +72,7 @@ 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 void common_signal_handler(PEXCEPTION_POINTERS pointers, int code, native_context_t *ucontext); @@ -92,7 +93,12 @@ 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; +// Pipe used for sending signal notifications to a helper thread +int g_signalPipe[2] = { 0, 0 }; + +DWORD g_dwExternalSignalHandlerThreadId = 0; /* public function definitions ************************************************/ @@ -179,6 +185,13 @@ void SEHCleanupSignals() restore_signal(SIGSEGV, &g_previous_sigsegv); restore_signal(SIGINT, &g_previous_sigint); restore_signal(SIGQUIT, &g_previous_sigquit); + + // Only restore if the signal handler thread was started and + // the previous handler was saved. + if (g_dwExternalSignalHandlerThreadId != 0) + { + restore_signal(SIGTERM, &g_previous_sigterm); + } } /* internal function definitions **********************************************/ @@ -494,6 +507,62 @@ static void sigquit_handler(int code, siginfo_t *siginfo, void *context) kill(gPID, code); } +/*++ + Function : + HandleExternalSignal + + Write to a pipe to kick off handling of the signal. + + Parameters : + signalCode - code of the external signal + + (no return value) +--*/ +static void HandleExternalSignal(int signalCode) +{ + BYTE signalCodeByte = (BYTE)signalCode; + ssize_t writtenBytes; + do + { + writtenBytes = write(g_signalPipe[1], &signalCodeByte, 1); + } + while ((writtenBytes == -1) && (errno == EINTR)); + + if (writtenBytes == -1) + { + // Fatal error + PROCAbort(); + } +} + +/*++ +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()) + { + HandleExternalSignal(code); + } + else + { + TRACE("SIGTERM signal was unhandled; chaining to previous sigaction\n"); + + if (g_previous_sigterm.sa_sigaction != NULL) + { + g_previous_sigterm.sa_sigaction(code, siginfo, context); + } + } +} + #ifdef INJECT_ACTIVATION_SIGNAL /*++ Function : @@ -714,4 +783,111 @@ void restore_signal(int signal_id, struct sigaction *previousAction) } } +static +DWORD +PALAPI +ExternalSignalHandlerThreadRoutine( + PVOID + ); + +PAL_ERROR +StartExternalSignalHandlerThread( + CPalThread *pthr) +{ + PAL_ERROR palError = NO_ERROR; + +#ifndef DO_NOT_USE_SIGNAL_HANDLING_THREAD + HANDLE hThread; + + if (pipe(g_signalPipe) != 0) + { + palError = ERROR_CANNOT_MAKE; + goto done; + } + + palError = InternalCreateThread( + pthr, + NULL, + 0, + ExternalSignalHandlerThreadRoutine, + NULL, + 0, + SignalHandlerThread, // Want no_suspend variant + &g_dwExternalSignalHandlerThreadId, + &hThread + ); + + if (palError != NO_ERROR) + { + ERROR("Failure creating external signal handler thread (%d)\n", palError); + goto done; + } + + InternalCloseHandle(pthr, hThread); + + handle_signal(SIGTERM, sigterm_handler, &g_previous_sigterm); +#endif // DO_NOT_USE_SIGNAL_HANDLING_THREAD + +done: + + return palError; +} + +static +DWORD +PALAPI +ExternalSignalHandlerThreadRoutine( + PVOID + ) +{ + DWORD dwThreadId; + bool fContinue = TRUE; + HANDLE hThread; + PAL_ERROR palError = NO_ERROR; + CPalThread *pthr = InternalGetCurrentThread(); + + // + // Wait for a signal to occur + // + + while (fContinue) + { + BYTE signalCode; + ssize_t bytesRead; + + do + { + bytesRead = read(g_signalPipe[0], &signalCode, 1); + } + while ((bytesRead == -1) && (errno == EINTR)); + + if (bytesRead == -1) + { + // Fatal error + PROCAbort(); + } + + switch (signalCode) + { + case SIGTERM: + { + SEHHandleTerminationRequest(); + } + + default: + ASSERT("Unexpected signal %d in signal thread\n", signalCode); + PROCAbort(); + break; + } + } + + // + // Perform an immediate (non-graceful) shutdown + // + + _exit(EXIT_FAILURE); + + return 0; +} + #endif // !HAVE_MACH_EXCEPTIONS diff --git a/src/pal/src/include/pal/process.h b/src/pal/src/include/pal/process.h index bd78f9bc1208..71396c458e99 100644 --- a/src/pal/src/include/pal/process.h +++ b/src/pal/src/include/pal/process.h @@ -134,7 +134,7 @@ void PROCAbort(); Function: PROCNotifyProcessShutdown - Calls the abort handler to do any shutdown cleanup. Call be + Calls the abort handler to do any shutdown cleanup. Can be called from the unhandled native exception handler. (no return value) diff --git a/src/pal/src/include/pal/seh.hpp b/src/pal/src/include/pal/seh.hpp index 4fa38359fe78..4b3aa1723328 100644 --- a/src/pal/src/include/pal/seh.hpp +++ b/src/pal/src/include/pal/seh.hpp @@ -74,6 +74,18 @@ Return value: VOID SEHProcessException(PEXCEPTION_POINTERS pointers); +/*++ +Function: + SEHHandleTerminationRequest + + Send a process termination request to a registered handler. + +Parameters: + None +--*/ +VOID +SEHHandleTerminationRequest(); + #if !HAVE_MACH_EXCEPTIONS // TODO: Implement for Mach exceptions. Not in CoreCLR surface area. /*++ diff --git a/src/pal/src/thread/process.cpp b/src/pal/src/thread/process.cpp index 0de235e59c36..f6a4e0a0062b 100644 --- a/src/pal/src/thread/process.cpp +++ b/src/pal/src/thread/process.cpp @@ -2673,7 +2673,7 @@ DestroyProcessModules(IN ProcessModules *listHead) Function: PROCNotifyProcessShutdown - Calls the abort handler to do any shutdown cleanup. Call be called + Calls the abort handler to do any shutdown cleanup. Can be called from the unhandled native exception handler. (no return value) diff --git a/src/vm/exceptionhandling.cpp b/src/vm/exceptionhandling.cpp index c3c5db20730f..b347513b3330 100644 --- a/src/vm/exceptionhandling.cpp +++ b/src/vm/exceptionhandling.cpp @@ -139,6 +139,11 @@ static inline void UpdatePerformanceMetrics(CrawlFrame *pcfThisFrame, BOOL bIsRe ETW::ExceptionLog::ExceptionThrown(pcfThisFrame, bIsRethrownException, bIsNewException); } +void ShutdownEEAndExitProcess() +{ + ForceEEShutdown(SCA_ExitProcessWhenShutdownComplete); +} + void InitializeExceptionHandling() { EH_LOG((LL_INFO100, "InitializeExceptionHandling(): ExceptionTracker size: 0x%x bytes\n", sizeof(ExceptionTracker))); @@ -158,6 +163,9 @@ void InitializeExceptionHandling() // Register handler for determining whether the specified IP has code that is a GC marker for GCCover PAL_SetGetGcMarkerExceptionCode(GetGcMarkerExceptionCode); + + // Register handler for termination requests (e.g. SIGTERM) + PAL_SetTerminationRequestHandler(ShutdownEEAndExitProcess); #endif // FEATURE_PAL }