AutorÃa | Ultima modificación | Ver Log |
// Copyright 2019 Google
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "Crashlytics/Crashlytics/Handlers/FIRCLSSignal.h"
#include "Crashlytics/Crashlytics/Components/FIRCLSGlobals.h"
#include "Crashlytics/Crashlytics/Handlers/FIRCLSHandler.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSUtility.h"
#include <dlfcn.h>
#include <stdlib.h>
#if CLS_SIGNAL_SUPPORTED
static const int FIRCLSFatalSignals[FIRCLSSignalCount] = {SIGABRT, SIGBUS, SIGFPE, SIGILL,
SIGSEGV, SIGSYS, SIGTRAP};
#if CLS_USE_SIGALTSTACK
static void FIRCLSSignalInstallAltStack(FIRCLSSignalReadContext *roContext);
#endif
static void FIRCLSSignalInstallHandlers(FIRCLSSignalReadContext *roContext);
static void FIRCLSSignalHandler(int signal, siginfo_t *info, void *uapVoid);
void FIRCLSSignalInitialize(FIRCLSSignalReadContext *roContext) {
if (!FIRCLSUnlinkIfExists(roContext->path)) {
FIRCLSSDKLog("Unable to reset the signal log file %s\n", strerror(errno));
}
#if CLS_USE_SIGALTSTACK
FIRCLSSignalInstallAltStack(roContext);
#endif
FIRCLSSignalInstallHandlers(roContext);
#if TARGET_IPHONE_SIMULATOR
// prevent the OpenGL stack (by way of OpenGLES.framework/libLLVMContainer.dylib) from installing
// signal handlers that do not chain back
// TODO: I don't believe this is necessary as of recent iOS releases
bool *ptr = dlsym(RTLD_DEFAULT, "_ZN4llvm23DisablePrettyStackTraceE");
if (ptr) {
*ptr = true;
}
#endif
}
void FIRCLSSignalEnumerateHandledSignals(void (^block)(int idx, int signal)) {
for (int i = 0; i < FIRCLSSignalCount; ++i) {
block(i, FIRCLSFatalSignals[i]);
}
}
#if CLS_USE_SIGALTSTACK
static void FIRCLSSignalInstallAltStack(FIRCLSSignalReadContext *roContext) {
stack_t signalStack;
stack_t originalStack;
signalStack.ss_sp = _firclsContext.readonly->signalStack;
signalStack.ss_size = CLS_SIGNAL_HANDLER_STACK_SIZE;
signalStack.ss_flags = 0;
if (sigaltstack(&signalStack, &originalStack) != 0) {
FIRCLSSDKLog("Unable to setup stack %s\n", strerror(errno));
return;
}
roContext->originalStack.ss_sp = NULL;
roContext->originalStack = originalStack;
}
#endif
static void FIRCLSSignalInstallHandlers(FIRCLSSignalReadContext *roContext) {
FIRCLSSignalEnumerateHandledSignals(^(int idx, int signal) {
struct sigaction action;
struct sigaction previousAction;
action.sa_sigaction = FIRCLSSignalHandler;
// SA_RESETHAND seems like it would be great, but it doesn't appear to
// work correctly. After taking a signal, causing another identical signal in
// the handler will *not* cause the default handler to be invokved (which should
// terminate the process). I've found some evidence that others have seen this
// behavior on MAC OS X.
action.sa_flags = SA_SIGINFO | SA_ONSTACK;
sigemptyset(&action.sa_mask);
previousAction.sa_sigaction = NULL;
if (sigaction(signal, &action, &previousAction) != 0) {
FIRCLSSDKLog("Unable to install handler for %d (%s)\n", signal, strerror(errno));
}
// store the last action, so it can be recalled
roContext->originalActions[idx].sa_sigaction = NULL;
if (previousAction.sa_sigaction) {
roContext->originalActions[idx] = previousAction;
}
});
}
void FIRCLSSignalCheckHandlers(void) {
if (_firclsContext.readonly->debuggerAttached) {
return;
}
FIRCLSSignalEnumerateHandledSignals(^(int idx, int signal) {
struct sigaction previousAction;
Dl_info info;
void *ptr;
if (sigaction(signal, 0, &previousAction) != 0) {
fprintf(stderr, "Unable to read signal handler\n");
return;
}
ptr = previousAction.__sigaction_u.__sa_handler;
const char *signalName = NULL;
const char *codeName = NULL;
FIRCLSSignalNameLookup(signal, 0, &signalName, &codeName);
if (ptr == FIRCLSSignalHandler) {
return;
}
const char *name = NULL;
if (dladdr(ptr, &info) != 0) {
name = info.dli_sname;
}
fprintf(stderr,
"[Crashlytics] The signal %s has a non-Crashlytics handler (%s). This will interfere "
"with reporting.\n",
signalName, name);
});
}
void FIRCLSSignalSafeRemoveHandlers(bool includingAbort) {
FIRCLSSignalEnumerateHandledSignals(^(int idx, int signal) {
struct sigaction sa;
if (!includingAbort && (signal == SIGABRT)) {
return;
}
sa.sa_handler = SIG_DFL;
sigemptyset(&sa.sa_mask);
if (sigaction(signal, &sa, NULL) != 0) {
FIRCLSSDKLog("Unable to set default handler for %d (%s)\n", signal, strerror(errno));
}
});
}
bool FIRCLSSignalSafeInstallPreexistingHandlers(FIRCLSSignalReadContext *roContext,
const int signal,
siginfo_t *info,
void *uapVoid) {
__block bool success = true;
FIRCLSSignalSafeRemoveHandlers(true);
#if CLS_USE_SIGALTSTACK
// re-install the original stack, if needed
if (roContext->originalStack.ss_sp) {
if (sigaltstack(&roContext->originalStack, 0) != 0) {
FIRCLSSDKLog("Unable to setup stack %s\n", strerror(errno));
return false;
}
}
#endif
// re-install the original handlers, if any
FIRCLSSignalEnumerateHandledSignals(^(int idx, int currentSignal) {
if (roContext->originalActions[idx].sa_sigaction == NULL) {
return;
}
if (sigaction(currentSignal, &roContext->originalActions[idx], 0) != 0) {
FIRCLSSDKLog("Unable to install handler for %d (%s)\n", currentSignal, strerror(errno));
success = false;
}
// invoke original handler for current signal
if (signal < 0) {
return;
}
if (signal == currentSignal) {
roContext->originalActions[idx].sa_sigaction(signal, info, uapVoid);
}
});
return success;
}
void FIRCLSSignalNameLookup(int number, int code, const char **name, const char **codeName) {
if (!name || !codeName) {
return;
}
*codeName = NULL;
switch (number) {
case SIGABRT:
*name = "SIGABRT";
*codeName = "ABORT";
break;
case SIGBUS:
*name = "SIGBUS";
break;
case SIGFPE:
*name = "SIGFPE";
break;
case SIGILL:
*name = "SIGILL";
break;
case SIGSEGV:
*name = "SIGSEGV";
break;
case SIGSYS:
*name = "SIGSYS";
break;
case SIGTRAP:
*name = "SIGTRAP";
break;
default:
*name = "UNKNOWN";
break;
}
}
static void FIRCLSSignalRecordSignal(int savedErrno, siginfo_t *info, void *uapVoid) {
if (!_firclsContext.readonly) {
return;
}
if (FIRCLSContextMarkAndCheckIfCrashed()) {
FIRCLSSDKLog("Error: aborting signal handler because crash has already occurred");
exit(1);
return;
}
FIRCLSFile file;
if (!FIRCLSFileInitWithPath(&file, _firclsContext.readonly->signal.path, false)) {
FIRCLSSDKLog("Unable to open signal file\n");
return;
}
FIRCLSFileWriteSectionStart(&file, "signal");
FIRCLSFileWriteHashStart(&file);
if (FIRCLSIsValidPointer(info)) {
FIRCLSFileWriteHashEntryUint64(&file, "number", info->si_signo);
FIRCLSFileWriteHashEntryUint64(&file, "code", info->si_code);
FIRCLSFileWriteHashEntryUint64(&file, "address", (uint64_t)info->si_addr);
const char *name = NULL;
const char *codeName = NULL;
FIRCLSSignalNameLookup(info->si_signo, info->si_code, &name, &codeName);
FIRCLSFileWriteHashEntryString(&file, "name", name);
FIRCLSFileWriteHashEntryString(&file, "code_name", codeName);
}
FIRCLSFileWriteHashEntryUint64(&file, "errno", savedErrno);
FIRCLSFileWriteHashEntryUint64(&file, "time", time(NULL));
FIRCLSFileWriteHashEnd(&file);
FIRCLSFileWriteSectionEnd(&file);
FIRCLSHandler(&file, mach_thread_self(), uapVoid);
FIRCLSFileClose(&file);
}
static void FIRCLSSignalHandler(int signal, siginfo_t *info, void *uapVoid) {
int savedErrno;
sigset_t set;
// save errno, both because it is interesting, and so we can restore it afterwards
savedErrno = errno;
errno = 0;
FIRCLSSDKLog("Signal: %d\n", signal);
// it is important to do this before unmasking signals, otherwise we can get
// called in a loop
FIRCLSSignalSafeRemoveHandlers(true);
sigfillset(&set);
if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
FIRCLSSDKLog("Unable to unmask signals - we risk infinite recursion here\n");
}
// check info and uapVoid, and set them to appropriate values if invalid. This can happen
// if we have been called without the SA_SIGINFO flag set
if (!FIRCLSIsValidPointer(info)) {
info = NULL;
}
if (!FIRCLSIsValidPointer(uapVoid)) {
uapVoid = NULL;
}
FIRCLSSignalRecordSignal(savedErrno, info, uapVoid);
// re-install original handlers
if (_firclsContext.readonly) {
FIRCLSSignalSafeInstallPreexistingHandlers(&_firclsContext.readonly->signal, signal, info,
uapVoid);
}
// restore errno
errno = savedErrno;
}
#endif