1//===-- working_set_posix.cpp -----------------------------------*- C++ -*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file is a part of EfficiencySanitizer, a family of performance tuners.
11//
12// POSIX-specific working set tool code.
13//===----------------------------------------------------------------------===//
14
15#include "working_set.h"
16#include "esan_flags.h"
17#include "esan_shadow.h"
18#include "sanitizer_common/sanitizer_common.h"
19#include "sanitizer_common/sanitizer_linux.h"
20#include <signal.h>
21#include <sys/mman.h>
22
23namespace __esan {
24
25// We only support regular POSIX threads with a single signal handler
26// for the whole process == thread group.
27// Thus we only need to store one app signal handler.
28// FIXME: Store and use any alternate stack and signal flags set by
29// the app.  For now we just call the app handler from our handler.
30static __sanitizer_sigaction AppSigAct;
31
32bool processWorkingSetSignal(int SigNum, void (*Handler)(int),
33                             void (**Result)(int)) {
34  VPrintf(2, "%s: %d\n", __FUNCTION__, SigNum);
35  if (SigNum == SIGSEGV) {
36    *Result = AppSigAct.handler;
37    AppSigAct.sigaction = (void (*)(int, void*, void*))Handler;
38    return false; // Skip real call.
39  }
40  return true;
41}
42
43bool processWorkingSetSigaction(int SigNum, const void *ActVoid,
44                                void *OldActVoid) {
45  VPrintf(2, "%s: %d\n", __FUNCTION__, SigNum);
46  if (SigNum == SIGSEGV) {
47    const struct sigaction *Act = (const struct sigaction *) ActVoid;
48    struct sigaction *OldAct = (struct sigaction *) OldActVoid;
49    if (OldAct)
50      internal_memcpy(OldAct, &AppSigAct, sizeof(OldAct));
51    if (Act)
52      internal_memcpy(&AppSigAct, Act, sizeof(AppSigAct));
53    return false; // Skip real call.
54  }
55  return true;
56}
57
58bool processWorkingSetSigprocmask(int How, void *Set, void *OldSet) {
59  VPrintf(2, "%s\n", __FUNCTION__);
60  // All we need to do is ensure that SIGSEGV is not blocked.
61  // FIXME: we are not fully transparent as we do not pretend that
62  // SIGSEGV is still blocked on app queries: that would require
63  // per-thread mask tracking.
64  if (Set && (How == SIG_BLOCK || How == SIG_SETMASK)) {
65    if (internal_sigismember((__sanitizer_sigset_t *)Set, SIGSEGV)) {
66      VPrintf(1, "%s: removing SIGSEGV from the blocked set\n", __FUNCTION__);
67      internal_sigdelset((__sanitizer_sigset_t *)Set, SIGSEGV);
68    }
69  }
70  return true;
71}
72
73static void reinstateDefaultHandler(int SigNum) {
74  __sanitizer_sigaction SigAct;
75  internal_memset(&SigAct, 0, sizeof(SigAct));
76  SigAct.sigaction = (void (*)(int, void*, void*)) SIG_DFL;
77  int Res = internal_sigaction(SigNum, &SigAct, nullptr);
78  CHECK(Res == 0);
79  VPrintf(1, "Unregistered for %d handler\n", SigNum);
80}
81
82// If this is a shadow fault, we handle it here; otherwise, we pass it to the
83// app to handle it just as the app would do without our tool in place.
84static void handleMemoryFault(int SigNum, void *Info, void *Ctx) {
85  if (SigNum == SIGSEGV) {
86    // We rely on si_addr being filled in (thus we do not support old kernels).
87    siginfo_t *SigInfo = (siginfo_t *)Info;
88    uptr Addr = (uptr)SigInfo->si_addr;
89    if (isShadowMem(Addr)) {
90      VPrintf(3, "Shadow fault @%p\n", Addr);
91      uptr PageSize = GetPageSizeCached();
92      int Res = internal_mprotect((void *)RoundDownTo(Addr, PageSize),
93                                  PageSize, PROT_READ|PROT_WRITE);
94      CHECK(Res == 0);
95    } else if (AppSigAct.sigaction) {
96      // FIXME: For simplicity we ignore app options including its signal stack
97      // (we just use ours) and all the delivery flags.
98      AppSigAct.sigaction(SigNum, Info, Ctx);
99    } else {
100      // Crash instead of spinning with infinite faults.
101      reinstateDefaultHandler(SigNum);
102    }
103  } else
104    UNREACHABLE("signal not registered");
105}
106
107void registerMemoryFaultHandler() {
108  // We do not use an alternate signal stack, as doing so would require
109  // setting it up for each app thread.
110  // FIXME: This could result in problems with emulating the app's signal
111  // handling if the app relies on an alternate stack for SIGSEGV.
112
113  // We require that SIGSEGV is not blocked.  We use a sigprocmask
114  // interceptor to ensure that in the future.  Here we ensure it for
115  // the current thread.  We assume there are no other threads at this
116  // point during initialization, or that at least they do not block
117  // SIGSEGV.
118  __sanitizer_sigset_t SigSet;
119  internal_sigemptyset(&SigSet);
120  internal_sigprocmask(SIG_BLOCK, &SigSet, nullptr);
121
122  __sanitizer_sigaction SigAct;
123  internal_memset(&SigAct, 0, sizeof(SigAct));
124  SigAct.sigaction = handleMemoryFault;
125  // We want to handle nested signals b/c we need to handle a
126  // shadow fault in an app signal handler.
127  SigAct.sa_flags = SA_SIGINFO | SA_NODEFER;
128  int Res = internal_sigaction(SIGSEGV, &SigAct, &AppSigAct);
129  CHECK(Res == 0);
130  VPrintf(1, "Registered for SIGSEGV handler\n");
131}
132
133} // namespace __esan
134