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 void dumpReport(uintptr_t ErrorPtr, const gwp_asan::AllocatorState *State, 103 const gwp_asan::AllocationMetadata *Metadata, 104 SegvBacktrace_t SegvBacktrace, Printf_t Printf, 105 PrintBacktrace_t PrintBacktrace, void *Context) { 106 assert(State && "dumpReport missing Allocator State."); 107 assert(Metadata && "dumpReport missing Metadata."); 108 assert(Printf && "dumpReport missing Printf."); 109 110 if (!__gwp_asan_error_is_mine(State, ErrorPtr)) 111 return; 112 113 Printf("*** GWP-ASan detected a memory error ***\n"); 114 ScopedEndOfReportDecorator Decorator(Printf); 115 116 uintptr_t InternalErrorPtr = __gwp_asan_get_internal_crash_address(State); 117 if (InternalErrorPtr != 0u) 118 ErrorPtr = InternalErrorPtr; 119 120 Error E = __gwp_asan_diagnose_error(State, Metadata, ErrorPtr); 121 122 if (E == Error::UNKNOWN) { 123 Printf("GWP-ASan cannot provide any more information about this error. " 124 "This may occur due to a wild memory access into the GWP-ASan pool, " 125 "or an overflow/underflow that is > 512B in length.\n"); 126 return; 127 } 128 129 const gwp_asan::AllocationMetadata *AllocMeta = 130 __gwp_asan_get_metadata(State, Metadata, ErrorPtr); 131 132 // Print the error header. 133 printHeader(E, ErrorPtr, AllocMeta, Printf); 134 135 // Print the fault backtrace. 136 static constexpr unsigned kMaximumStackFramesForCrashTrace = 512; 137 uintptr_t Trace[kMaximumStackFramesForCrashTrace]; 138 size_t TraceLength = 139 SegvBacktrace(Trace, kMaximumStackFramesForCrashTrace, Context); 140 141 PrintBacktrace(Trace, TraceLength, Printf); 142 143 if (AllocMeta == nullptr) 144 return; 145 146 // Maybe print the deallocation trace. 147 if (__gwp_asan_is_deallocated(AllocMeta)) { 148 uint64_t ThreadID = __gwp_asan_get_deallocation_thread_id(AllocMeta); 149 if (ThreadID == gwp_asan::kInvalidThreadID) 150 Printf("0x%zx was deallocated by thread <unknown> here:\n", ErrorPtr); 151 else 152 Printf("0x%zx was deallocated by thread %zu here:\n", ErrorPtr, ThreadID); 153 TraceLength = __gwp_asan_get_deallocation_trace( 154 AllocMeta, Trace, kMaximumStackFramesForCrashTrace); 155 PrintBacktrace(Trace, TraceLength, Printf); 156 } 157 158 // Print the allocation trace. 159 uint64_t ThreadID = __gwp_asan_get_allocation_thread_id(AllocMeta); 160 if (ThreadID == gwp_asan::kInvalidThreadID) 161 Printf("0x%zx was allocated by thread <unknown> here:\n", ErrorPtr); 162 else 163 Printf("0x%zx was allocated by thread %zu here:\n", ErrorPtr, ThreadID); 164 TraceLength = __gwp_asan_get_allocation_trace( 165 AllocMeta, Trace, kMaximumStackFramesForCrashTrace); 166 PrintBacktrace(Trace, TraceLength, Printf); 167 } 168 169 struct sigaction PreviousHandler; 170 bool SignalHandlerInstalled; 171 gwp_asan::GuardedPoolAllocator *GPAForSignalHandler; 172 Printf_t PrintfForSignalHandler; 173 PrintBacktrace_t PrintBacktraceForSignalHandler; 174 SegvBacktrace_t BacktraceForSignalHandler; 175 176 static void sigSegvHandler(int sig, siginfo_t *info, void *ucontext) { 177 if (GPAForSignalHandler) { 178 GPAForSignalHandler->stop(); 179 180 dumpReport(reinterpret_cast<uintptr_t>(info->si_addr), 181 GPAForSignalHandler->getAllocatorState(), 182 GPAForSignalHandler->getMetadataRegion(), 183 BacktraceForSignalHandler, PrintfForSignalHandler, 184 PrintBacktraceForSignalHandler, ucontext); 185 } 186 187 // Process any previous handlers. 188 if (PreviousHandler.sa_flags & SA_SIGINFO) { 189 PreviousHandler.sa_sigaction(sig, info, ucontext); 190 } else if (PreviousHandler.sa_handler == SIG_DFL) { 191 // If the previous handler was the default handler, cause a core dump. 192 signal(SIGSEGV, SIG_DFL); 193 raise(SIGSEGV); 194 } else if (PreviousHandler.sa_handler == SIG_IGN) { 195 // If the previous segv handler was SIGIGN, crash iff we were responsible 196 // for the crash. 197 if (__gwp_asan_error_is_mine(GPAForSignalHandler->getAllocatorState(), 198 reinterpret_cast<uintptr_t>(info->si_addr))) { 199 signal(SIGSEGV, SIG_DFL); 200 raise(SIGSEGV); 201 } 202 } else { 203 PreviousHandler.sa_handler(sig); 204 } 205 } 206 } // anonymous namespace 207 208 namespace gwp_asan { 209 namespace segv_handler { 210 211 void installSignalHandlers(gwp_asan::GuardedPoolAllocator *GPA, Printf_t Printf, 212 PrintBacktrace_t PrintBacktrace, 213 SegvBacktrace_t SegvBacktrace) { 214 assert(GPA && "GPA wasn't provided to installSignalHandlers."); 215 assert(Printf && "Printf wasn't provided to installSignalHandlers."); 216 assert(PrintBacktrace && 217 "PrintBacktrace wasn't provided to installSignalHandlers."); 218 assert(SegvBacktrace && 219 "SegvBacktrace wasn't provided to installSignalHandlers."); 220 GPAForSignalHandler = GPA; 221 PrintfForSignalHandler = Printf; 222 PrintBacktraceForSignalHandler = PrintBacktrace; 223 BacktraceForSignalHandler = SegvBacktrace; 224 225 struct sigaction Action = {}; 226 Action.sa_sigaction = sigSegvHandler; 227 Action.sa_flags = SA_SIGINFO; 228 sigaction(SIGSEGV, &Action, &PreviousHandler); 229 SignalHandlerInstalled = true; 230 } 231 232 void uninstallSignalHandlers() { 233 if (SignalHandlerInstalled) { 234 sigaction(SIGSEGV, &PreviousHandler, nullptr); 235 SignalHandlerInstalled = false; 236 } 237 } 238 } // namespace segv_handler 239 } // namespace gwp_asan 240