llvm-project/compiler-rt/lib/esan/working_set_posix.cpp

135 lines
5.1 KiB
C++

//===-- working_set_posix.cpp -----------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of EfficiencySanitizer, a family of performance tuners.
//
// POSIX-specific working set tool code.
//===----------------------------------------------------------------------===//
#include "working_set.h"
#include "esan_flags.h"
#include "esan_shadow.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_linux.h"
#include <signal.h>
#include <sys/mman.h>
namespace __esan {
// We only support regular POSIX threads with a single signal handler
// for the whole process == thread group.
// Thus we only need to store one app signal handler.
// FIXME: Store and use any alternate stack and signal flags set by
// the app. For now we just call the app handler from our handler.
static __sanitizer_sigaction AppSigAct;
bool processWorkingSetSignal(int SigNum, void (*Handler)(int),
void (**Result)(int)) {
VPrintf(2, "%s: %d\n", __FUNCTION__, SigNum);
if (SigNum == SIGSEGV) {
*Result = AppSigAct.handler;
AppSigAct.sigaction = (decltype(AppSigAct.sigaction))Handler;
return false; // Skip real call.
}
return true;
}
bool processWorkingSetSigaction(int SigNum, const void *ActVoid,
void *OldActVoid) {
VPrintf(2, "%s: %d\n", __FUNCTION__, SigNum);
if (SigNum == SIGSEGV) {
const struct sigaction *Act = (const struct sigaction *) ActVoid;
struct sigaction *OldAct = (struct sigaction *) OldActVoid;
if (OldAct)
internal_memcpy(OldAct, &AppSigAct, sizeof(OldAct));
if (Act)
internal_memcpy(&AppSigAct, Act, sizeof(AppSigAct));
return false; // Skip real call.
}
return true;
}
bool processWorkingSetSigprocmask(int How, void *Set, void *OldSet) {
VPrintf(2, "%s\n", __FUNCTION__);
// All we need to do is ensure that SIGSEGV is not blocked.
// FIXME: we are not fully transparent as we do not pretend that
// SIGSEGV is still blocked on app queries: that would require
// per-thread mask tracking.
if (Set && (How == SIG_BLOCK || How == SIG_SETMASK)) {
if (internal_sigismember((__sanitizer_sigset_t *)Set, SIGSEGV)) {
VPrintf(1, "%s: removing SIGSEGV from the blocked set\n", __FUNCTION__);
internal_sigdelset((__sanitizer_sigset_t *)Set, SIGSEGV);
}
}
return true;
}
static void reinstateDefaultHandler(int SigNum) {
__sanitizer_sigaction SigAct;
internal_memset(&SigAct, 0, sizeof(SigAct));
SigAct.sigaction = (decltype(SigAct.sigaction))SIG_DFL;
int Res = internal_sigaction(SigNum, &SigAct, nullptr);
CHECK(Res == 0);
VPrintf(1, "Unregistered for %d handler\n", SigNum);
}
// If this is a shadow fault, we handle it here; otherwise, we pass it to the
// app to handle it just as the app would do without our tool in place.
static void handleMemoryFault(int SigNum, __sanitizer_siginfo *Info,
void *Ctx) {
if (SigNum == SIGSEGV) {
// We rely on si_addr being filled in (thus we do not support old kernels).
siginfo_t *SigInfo = (siginfo_t *)Info;
uptr Addr = (uptr)SigInfo->si_addr;
if (isShadowMem(Addr)) {
VPrintf(3, "Shadow fault @%p\n", Addr);
uptr PageSize = GetPageSizeCached();
int Res = internal_mprotect((void *)RoundDownTo(Addr, PageSize),
PageSize, PROT_READ|PROT_WRITE);
CHECK(Res == 0);
} else if (AppSigAct.sigaction) {
// FIXME: For simplicity we ignore app options including its signal stack
// (we just use ours) and all the delivery flags.
AppSigAct.sigaction(SigNum, Info, Ctx);
} else {
// Crash instead of spinning with infinite faults.
reinstateDefaultHandler(SigNum);
}
} else
UNREACHABLE("signal not registered");
}
void registerMemoryFaultHandler() {
// We do not use an alternate signal stack, as doing so would require
// setting it up for each app thread.
// FIXME: This could result in problems with emulating the app's signal
// handling if the app relies on an alternate stack for SIGSEGV.
// We require that SIGSEGV is not blocked. We use a sigprocmask
// interceptor to ensure that in the future. Here we ensure it for
// the current thread. We assume there are no other threads at this
// point during initialization, or that at least they do not block
// SIGSEGV.
__sanitizer_sigset_t SigSet;
internal_sigemptyset(&SigSet);
internal_sigprocmask(SIG_BLOCK, &SigSet, nullptr);
__sanitizer_sigaction SigAct;
internal_memset(&SigAct, 0, sizeof(SigAct));
SigAct.sigaction = handleMemoryFault;
// We want to handle nested signals b/c we need to handle a
// shadow fault in an app signal handler.
SigAct.sa_flags = SA_SIGINFO | SA_NODEFER;
int Res = internal_sigaction(SIGSEGV, &SigAct, &AppSigAct);
CHECK(Res == 0);
VPrintf(1, "Registered for SIGSEGV handler\n");
}
} // namespace __esan