1 //===-- segv_handler_posix.cpp ----------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "gwp_asan/common.h" 10 #include "gwp_asan/crash_handler.h" 11 #include "gwp_asan/guarded_pool_allocator.h" 12 #include "gwp_asan/optional/segv_handler.h" 13 #include "gwp_asan/options.h" 14 15 // RHEL creates the PRIu64 format macro (for printing uint64_t's) only when this 16 // macro is defined before including <inttypes.h>. 17 #ifndef __STDC_FORMAT_MACROS 18 #define __STDC_FORMAT_MACROS 1 19 #endif 20 21 #include <assert.h> 22 #include <inttypes.h> 23 #include <signal.h> 24 #include <stdio.h> 25 26 using gwp_asan::AllocationMetadata; 27 using gwp_asan::Error; 28 using gwp_asan::GuardedPoolAllocator; 29 using gwp_asan::Printf_t; 30 using gwp_asan::backtrace::PrintBacktrace_t; 31 using gwp_asan::backtrace::SegvBacktrace_t; 32 33 namespace { 34 35 struct ScopedEndOfReportDecorator { 36 ScopedEndOfReportDecorator(gwp_asan::Printf_t Printf) : Printf(Printf) {} 37 ~ScopedEndOfReportDecorator() { Printf("*** End GWP-ASan report ***\n"); } 38 gwp_asan::Printf_t Printf; 39 }; 40 41 // Prints the provided error and metadata information. 42 void printHeader(Error E, uintptr_t AccessPtr, 43 const gwp_asan::AllocationMetadata *Metadata, 44 Printf_t Printf) { 45 // Print using intermediate strings. Platforms like Android don't like when 46 // you print multiple times to the same line, as there may be a newline 47 // appended to a log file automatically per Printf() call. 48 constexpr size_t kDescriptionBufferLen = 128; 49 char DescriptionBuffer[kDescriptionBufferLen] = ""; 50 51 bool AccessWasInBounds = false; 52 if (E != Error::UNKNOWN && Metadata != nullptr) { 53 uintptr_t Address = __gwp_asan_get_allocation_address(Metadata); 54 size_t Size = __gwp_asan_get_allocation_size(Metadata); 55 if (AccessPtr < Address) { 56 snprintf(DescriptionBuffer, kDescriptionBufferLen, 57 "(%zu byte%s to the left of a %zu-byte allocation at 0x%zx) ", 58 Address - AccessPtr, (Address - AccessPtr == 1) ? "" : "s", Size, 59 Address); 60 } else if (AccessPtr > Address) { 61 snprintf(DescriptionBuffer, kDescriptionBufferLen, 62 "(%zu byte%s to the right of a %zu-byte allocation at 0x%zx) ", 63 AccessPtr - Address, (AccessPtr - Address == 1) ? "" : "s", Size, 64 Address); 65 } else if (E == Error::DOUBLE_FREE) { 66 snprintf(DescriptionBuffer, kDescriptionBufferLen, 67 "(a %zu-byte allocation) ", Size); 68 } else { 69 AccessWasInBounds = true; 70 snprintf(DescriptionBuffer, kDescriptionBufferLen, 71 "(%zu byte%s into a %zu-byte allocation at 0x%zx) ", 72 AccessPtr - Address, (AccessPtr - Address == 1) ? "" : "s", Size, 73 Address); 74 } 75 } 76 77 // Possible number of digits of a 64-bit number: ceil(log10(2^64)) == 20. Add 78 // a null terminator, and round to the nearest 8-byte boundary. 79 uint64_t ThreadID = gwp_asan::getThreadID(); 80 constexpr size_t kThreadBufferLen = 24; 81 char ThreadBuffer[kThreadBufferLen]; 82 if (ThreadID == gwp_asan::kInvalidThreadID) 83 snprintf(ThreadBuffer, kThreadBufferLen, "<unknown>"); 84 else 85 snprintf(ThreadBuffer, kThreadBufferLen, "%" PRIu64, ThreadID); 86 87 const char *OutOfBoundsAndUseAfterFreeWarning = ""; 88 if (E == Error::USE_AFTER_FREE && !AccessWasInBounds) { 89 OutOfBoundsAndUseAfterFreeWarning = 90 " (warning: buffer overflow/underflow detected on a free()'d " 91 "allocation. This either means you have a buffer-overflow and a " 92 "use-after-free at the same time, or you have a long-lived " 93 "use-after-free bug where the allocation/deallocation metadata below " 94 "has already been overwritten and is likely bogus)"; 95 } 96 97 Printf("%s%s at 0x%zx %sby thread %s here:\n", gwp_asan::ErrorToString(E), 98 OutOfBoundsAndUseAfterFreeWarning, AccessPtr, DescriptionBuffer, 99 ThreadBuffer); 100 } 101 102 static bool HasReportedBadPoolAccess = false; 103 static const char *kUnknownCrashText = 104 "GWP-ASan cannot provide any more information about this error. This may " 105 "occur due to a wild memory access into the GWP-ASan pool, or an " 106 "overflow/underflow that is > 512B in length.\n"; 107 108 void dumpReport(uintptr_t ErrorPtr, const gwp_asan::AllocatorState *State, 109 const gwp_asan::AllocationMetadata *Metadata, 110 SegvBacktrace_t SegvBacktrace, Printf_t Printf, 111 PrintBacktrace_t PrintBacktrace, void *Context) { 112 assert(State && "dumpReport missing Allocator State."); 113 assert(Metadata && "dumpReport missing Metadata."); 114 assert(Printf && "dumpReport missing Printf."); 115 assert(__gwp_asan_error_is_mine(State, ErrorPtr) && 116 "dumpReport() called on a non-GWP-ASan error."); 117 118 uintptr_t InternalErrorPtr = 119 __gwp_asan_get_internal_crash_address(State, ErrorPtr); 120 if (InternalErrorPtr) 121 ErrorPtr = InternalErrorPtr; 122 123 const gwp_asan::AllocationMetadata *AllocMeta = 124 __gwp_asan_get_metadata(State, Metadata, ErrorPtr); 125 126 if (AllocMeta == nullptr) { 127 if (HasReportedBadPoolAccess) return; 128 HasReportedBadPoolAccess = true; 129 Printf("*** GWP-ASan detected a memory error ***\n"); 130 ScopedEndOfReportDecorator Decorator(Printf); 131 Printf(kUnknownCrashText); 132 return; 133 } 134 135 // It's unusual for a signal handler to be invoked multiple times for the same 136 // allocation, but it's possible in various scenarios, like: 137 // 1. A double-free or invalid-free was invoked in one thread at the same 138 // time as a buffer-overflow or use-after-free in another thread, or 139 // 2. Two threads do a use-after-free or buffer-overflow at the same time. 140 // In these instances, we've already dumped a report for this allocation, so 141 // skip dumping this issue as well. 142 if (AllocMeta->HasCrashed) 143 return; 144 145 Printf("*** GWP-ASan detected a memory error ***\n"); 146 ScopedEndOfReportDecorator Decorator(Printf); 147 148 Error E = __gwp_asan_diagnose_error(State, Metadata, ErrorPtr); 149 if (E == Error::UNKNOWN) { 150 Printf(kUnknownCrashText); 151 return; 152 } 153 154 // Print the error header. 155 printHeader(E, ErrorPtr, AllocMeta, Printf); 156 157 // Print the fault backtrace. 158 static constexpr unsigned kMaximumStackFramesForCrashTrace = 512; 159 uintptr_t Trace[kMaximumStackFramesForCrashTrace]; 160 size_t TraceLength = 161 SegvBacktrace(Trace, kMaximumStackFramesForCrashTrace, Context); 162 163 PrintBacktrace(Trace, TraceLength, Printf); 164 165 // Maybe print the deallocation trace. 166 if (__gwp_asan_is_deallocated(AllocMeta)) { 167 uint64_t ThreadID = __gwp_asan_get_deallocation_thread_id(AllocMeta); 168 if (ThreadID == gwp_asan::kInvalidThreadID) 169 Printf("0x%zx was deallocated by thread <unknown> here:\n", ErrorPtr); 170 else 171 Printf("0x%zx was deallocated by thread %zu here:\n", ErrorPtr, ThreadID); 172 TraceLength = __gwp_asan_get_deallocation_trace( 173 AllocMeta, Trace, kMaximumStackFramesForCrashTrace); 174 PrintBacktrace(Trace, TraceLength, Printf); 175 } 176 177 // Print the allocation trace. 178 uint64_t ThreadID = __gwp_asan_get_allocation_thread_id(AllocMeta); 179 if (ThreadID == gwp_asan::kInvalidThreadID) 180 Printf("0x%zx was allocated by thread <unknown> here:\n", ErrorPtr); 181 else 182 Printf("0x%zx was allocated by thread %zu here:\n", ErrorPtr, ThreadID); 183 TraceLength = __gwp_asan_get_allocation_trace( 184 AllocMeta, Trace, kMaximumStackFramesForCrashTrace); 185 PrintBacktrace(Trace, TraceLength, Printf); 186 } 187 188 struct sigaction PreviousHandler; 189 bool SignalHandlerInstalled; 190 bool RecoverableSignal; 191 gwp_asan::GuardedPoolAllocator *GPAForSignalHandler; 192 Printf_t PrintfForSignalHandler; 193 PrintBacktrace_t PrintBacktraceForSignalHandler; 194 SegvBacktrace_t BacktraceForSignalHandler; 195 196 static void sigSegvHandler(int sig, siginfo_t *info, void *ucontext) { 197 const gwp_asan::AllocatorState *State = 198 GPAForSignalHandler->getAllocatorState(); 199 void *FaultAddr = info->si_addr; 200 uintptr_t FaultAddrUPtr = reinterpret_cast<uintptr_t>(FaultAddr); 201 202 if (__gwp_asan_error_is_mine(State, FaultAddrUPtr)) { 203 GPAForSignalHandler->preCrashReport(FaultAddr); 204 205 dumpReport(FaultAddrUPtr, State, GPAForSignalHandler->getMetadataRegion(), 206 BacktraceForSignalHandler, PrintfForSignalHandler, 207 PrintBacktraceForSignalHandler, ucontext); 208 209 if (RecoverableSignal) { 210 GPAForSignalHandler->postCrashReportRecoverableOnly(FaultAddr); 211 return; 212 } 213 } 214 215 // Process any previous handlers as long as the crash wasn't a GWP-ASan crash 216 // in recoverable mode. 217 if (PreviousHandler.sa_flags & SA_SIGINFO) { 218 PreviousHandler.sa_sigaction(sig, info, ucontext); 219 } else if (PreviousHandler.sa_handler == SIG_DFL) { 220 // If the previous handler was the default handler, cause a core dump. 221 signal(SIGSEGV, SIG_DFL); 222 raise(SIGSEGV); 223 } else if (PreviousHandler.sa_handler == SIG_IGN) { 224 // If the previous segv handler was SIGIGN, crash iff we were responsible 225 // for the crash. 226 if (__gwp_asan_error_is_mine(GPAForSignalHandler->getAllocatorState(), 227 reinterpret_cast<uintptr_t>(info->si_addr))) { 228 signal(SIGSEGV, SIG_DFL); 229 raise(SIGSEGV); 230 } 231 } else { 232 PreviousHandler.sa_handler(sig); 233 } 234 } 235 } // anonymous namespace 236 237 namespace gwp_asan { 238 namespace segv_handler { 239 240 void installSignalHandlers(gwp_asan::GuardedPoolAllocator *GPA, Printf_t Printf, 241 PrintBacktrace_t PrintBacktrace, 242 SegvBacktrace_t SegvBacktrace, bool Recoverable) { 243 assert(GPA && "GPA wasn't provided to installSignalHandlers."); 244 assert(Printf && "Printf wasn't provided to installSignalHandlers."); 245 assert(PrintBacktrace && 246 "PrintBacktrace wasn't provided to installSignalHandlers."); 247 assert(SegvBacktrace && 248 "SegvBacktrace wasn't provided to installSignalHandlers."); 249 GPAForSignalHandler = GPA; 250 PrintfForSignalHandler = Printf; 251 PrintBacktraceForSignalHandler = PrintBacktrace; 252 BacktraceForSignalHandler = SegvBacktrace; 253 RecoverableSignal = Recoverable; 254 255 struct sigaction Action = {}; 256 Action.sa_sigaction = sigSegvHandler; 257 Action.sa_flags = SA_SIGINFO; 258 sigaction(SIGSEGV, &Action, &PreviousHandler); 259 SignalHandlerInstalled = true; 260 } 261 262 void uninstallSignalHandlers() { 263 if (SignalHandlerInstalled) { 264 sigaction(SIGSEGV, &PreviousHandler, nullptr); 265 SignalHandlerInstalled = false; 266 } 267 } 268 } // namespace segv_handler 269 } // namespace gwp_asan 270