|
| 1 | +#ifdef CAGE_SYSTEM_LINUX |
| 2 | + |
| 3 | + #include <csignal> |
| 4 | + #include <cstddef> |
| 5 | + #include <cstdio> |
| 6 | + #include <cstdlib> |
| 7 | + #include <cstring> |
| 8 | + #include <cerrno> |
| 9 | + #include <execinfo.h> |
| 10 | + #include <sys/mman.h> |
| 11 | + #include <ucontext.h> |
| 12 | + #include <unistd.h> |
| 13 | + |
| 14 | + #include <cage-core/core.h> |
| 15 | + |
| 16 | +namespace cage |
| 17 | +{ |
| 18 | + int crashHandlerLogFileFd = STDERR_FILENO; |
| 19 | + |
| 20 | + namespace |
| 21 | + { |
| 22 | + void *altStackMem = nullptr; |
| 23 | + constexpr int OldHandlersCount = 16; |
| 24 | + struct sigaction prevHandlers[OldHandlersCount] = {}; |
| 25 | + |
| 26 | + struct sigaction *prevHandler(int sig) |
| 27 | + { |
| 28 | + if (sig >= 0 && sig < OldHandlersCount) |
| 29 | + return &prevHandlers[sig]; |
| 30 | + return nullptr; |
| 31 | + } |
| 32 | + |
| 33 | + const char *sigToStr(int sig) |
| 34 | + { |
| 35 | + switch (sig) |
| 36 | + { |
| 37 | + case SIGSEGV: |
| 38 | + return "SIGSEGV"; |
| 39 | + case SIGABRT: |
| 40 | + return "SIGABRT"; |
| 41 | + case SIGFPE: |
| 42 | + return "SIGFPE"; |
| 43 | + case SIGILL: |
| 44 | + return "SIGILL"; |
| 45 | + case SIGBUS: |
| 46 | + return "SIGBUS"; |
| 47 | + case SIGTRAP: |
| 48 | + return "SIGTRAP"; |
| 49 | + case SIGTERM: |
| 50 | + return "SIGTERM"; |
| 51 | + default: |
| 52 | + return "UNKNOWN"; |
| 53 | + } |
| 54 | + } |
| 55 | + |
| 56 | + void safeWrite(const String &s) |
| 57 | + { |
| 58 | + write(crashHandlerLogFileFd, s.c_str(), s.length()); |
| 59 | + } |
| 60 | + |
| 61 | + void printStackTrace() |
| 62 | + { |
| 63 | + // backtrace (best-effort; not strictly async-signal-safe) |
| 64 | + static constexpr int kMaxFrames = 256; |
| 65 | + void *frames[kMaxFrames]; |
| 66 | + int n = backtrace(frames, kMaxFrames); |
| 67 | + safeWrite(Stringizer() + "stack trace:\n"); |
| 68 | + backtrace_symbols_fd(frames, n, crashHandlerLogFileFd); // writes directly to fd |
| 69 | + } |
| 70 | + |
| 71 | + void crashHandler(int sig, siginfo_t *si, void *uctx) |
| 72 | + { |
| 73 | + safeWrite(Stringizer() + "signal caught: " + sigToStr(sig) + " (" + sig + ")\n"); |
| 74 | + |
| 75 | + if (si) |
| 76 | + safeWrite(Stringizer() + "fault addr: " + (uintptr_t)si->si_addr + ", code: " + si->si_code + "\n"); |
| 77 | + |
| 78 | + printStackTrace(); |
| 79 | + |
| 80 | + if (struct sigaction *prev = prevHandler(sig)) |
| 81 | + { |
| 82 | + if (prev->sa_flags & SA_SIGINFO) |
| 83 | + { |
| 84 | + // previous handler expects 3 arguments |
| 85 | + prev->sa_sigaction(sig, si, uctx); |
| 86 | + } |
| 87 | + else if (prev->sa_handler == SIG_DFL || prev->sa_handler == SIG_IGN) |
| 88 | + { |
| 89 | + // default or ignore: you can reset signal to default and raise |
| 90 | + signal(sig, prev->sa_handler); |
| 91 | + raise(sig); |
| 92 | + } |
| 93 | + else |
| 94 | + { |
| 95 | + // simple handler with 1 argument |
| 96 | + prev->sa_handler(sig); |
| 97 | + } |
| 98 | + } |
| 99 | + |
| 100 | + // re-raise to trigger default handler (and core dump, if enabled) |
| 101 | + signal(sig, SIG_DFL); |
| 102 | + raise(sig); |
| 103 | + } |
| 104 | + |
| 105 | + void installAltStack() |
| 106 | + { |
| 107 | + static constexpr size_t kAltStackSz = 128 * 1024; // bigger than SIGSTKSZ for safety |
| 108 | + altStackMem = mmap(nullptr, kAltStackSz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); |
| 109 | + if (altStackMem == MAP_FAILED) |
| 110 | + altStackMem = std::malloc(kAltStackSz); // fallback to malloc |
| 111 | + stack_t ss{}; |
| 112 | + ss.ss_sp = altStackMem; |
| 113 | + ss.ss_size = kAltStackSz; |
| 114 | + ss.ss_flags = 0; |
| 115 | + if (sigaltstack(&ss, nullptr) != 0) |
| 116 | + { |
| 117 | + CAGE_LOG(SeverityEnum::Info, "crash-handler", strerror(errno)); |
| 118 | + CAGE_THROW_ERROR(Exception, "sigaltstack") |
| 119 | + } |
| 120 | + } |
| 121 | + |
| 122 | + void installHandler(int sig) |
| 123 | + { |
| 124 | + struct sigaction sa |
| 125 | + {}; |
| 126 | + sa.sa_sigaction = &crashHandler; |
| 127 | + sigemptyset(&sa.sa_mask); |
| 128 | + sa.sa_flags = SA_SIGINFO | SA_ONSTACK; |
| 129 | + if (sigaction(sig, &sa, prevHandler(sig)) != 0) |
| 130 | + { |
| 131 | + CAGE_LOG(SeverityEnum::Info, "crash-handler", strerror(errno)); |
| 132 | + CAGE_THROW_ERROR(Exception, "sigaction") |
| 133 | + } |
| 134 | + } |
| 135 | + |
| 136 | + struct SetupHandlers |
| 137 | + { |
| 138 | + SetupHandlers() |
| 139 | + { |
| 140 | + installAltStack(); |
| 141 | + int signals[] = { SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, SIGTRAP }; |
| 142 | + for (int s : signals) |
| 143 | + installHandler(s); |
| 144 | + } |
| 145 | + } setupHandlers; |
| 146 | + } |
| 147 | +} |
| 148 | + |
| 149 | +#endif // CAGE_SYSTEM_LINUX |
0 commit comments