| 1 | //===-- ubsan_diag.cpp ----------------------------------------------------===// |
| 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 | // Diagnostic reporting for the UBSan runtime. |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #include "ubsan_platform.h" |
| 14 | #if CAN_SANITIZE_UB |
| 15 | #include "ubsan_diag.h" |
| 16 | #include "ubsan_flags.h" |
| 17 | #include "ubsan_init.h" |
| 18 | #include "ubsan_monitor.h" |
| 19 | |
| 20 | #include "sanitizer_common/sanitizer_common.h" |
| 21 | #include "sanitizer_common/sanitizer_internal_defs.h" |
| 22 | #include "sanitizer_common/sanitizer_placement_new.h" |
| 23 | #include "sanitizer_common/sanitizer_report_decorator.h" |
| 24 | #include "sanitizer_common/sanitizer_stacktrace.h" |
| 25 | #include "sanitizer_common/sanitizer_stacktrace_printer.h" |
| 26 | #include "sanitizer_common/sanitizer_suppressions.h" |
| 27 | #include "sanitizer_common/sanitizer_symbolizer.h" |
| 28 | |
| 29 | #include <stdio.h> |
| 30 | |
| 31 | using namespace __ubsan; |
| 32 | |
| 33 | // Can be overriden in frontend. |
| 34 | SANITIZER_INTERFACE_WEAK_DEF(const char *, __ubsan_default_suppressions, void) { |
| 35 | return "" ; |
| 36 | } |
| 37 | |
| 38 | // UBSan is combined with runtimes that already provide this functionality |
| 39 | // (e.g., ASan) as well as runtimes that lack it (e.g., scudo). Tried to use |
| 40 | // weak linkage to resolve this issue which is not portable and breaks on |
| 41 | // Windows. |
| 42 | // TODO(yln): This is a temporary workaround. GetStackTrace functions will be |
| 43 | // removed in the future. |
| 44 | void ubsan_GetStackTrace(BufferedStackTrace *stack, uptr max_depth, uptr pc, |
| 45 | uptr bp, void *context, bool request_fast) { |
| 46 | uptr top = 0; |
| 47 | uptr bottom = 0; |
| 48 | GetThreadStackTopAndBottom(at_initialization: false, stack_top: &top, stack_bottom: &bottom); |
| 49 | bool fast = StackTrace::WillUseFastUnwind(request_fast_unwind: request_fast); |
| 50 | stack->Unwind(max_depth, pc, bp, context, stack_top: top, stack_bottom: bottom, request_fast_unwind: fast); |
| 51 | } |
| 52 | |
| 53 | static void PrintSymbolizedFrames(SymbolizedStack *Frames); |
| 54 | |
| 55 | static void MaybePrintStackTrace(const ReportOptions &Opts) { |
| 56 | // We assume that flags are already parsed, as UBSan runtime |
| 57 | // will definitely be called when we print the first diagnostics message. |
| 58 | if (!flags()->print_stacktrace) |
| 59 | return; |
| 60 | |
| 61 | // Reports to be symbolized from the device need to use a separate file. |
| 62 | if (Opts.FromOffload) { |
| 63 | if (!Opts.pc) |
| 64 | return; |
| 65 | SymbolizedStackHolder Frame(getReportLocation(PC: Opts.pc, FromOffload: true)); |
| 66 | PrintSymbolizedFrames(Frames: const_cast<SymbolizedStack *>(Frame.get())); |
| 67 | return; |
| 68 | } |
| 69 | |
| 70 | UNINITIALIZED BufferedStackTrace stack; |
| 71 | ubsan_GetStackTrace(stack: &stack, max_depth: kStackTraceMax, pc: Opts.pc, bp: Opts.bp, context: nullptr, |
| 72 | request_fast: common_flags()->fast_unwind_on_fatal); |
| 73 | stack.Print(); |
| 74 | } |
| 75 | |
| 76 | static const char *ConvertTypeToString(ErrorType Type) { |
| 77 | switch (Type) { |
| 78 | #define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) \ |
| 79 | case ErrorType::Name: \ |
| 80 | return SummaryKind; |
| 81 | #include "ubsan_checks.inc" |
| 82 | #undef UBSAN_CHECK |
| 83 | } |
| 84 | UNREACHABLE("unknown ErrorType!" ); |
| 85 | } |
| 86 | |
| 87 | static const char *ConvertTypeToFlagName(ErrorType Type) { |
| 88 | switch (Type) { |
| 89 | #define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) \ |
| 90 | case ErrorType::Name: \ |
| 91 | return FSanitizeFlagName; |
| 92 | #include "ubsan_checks.inc" |
| 93 | #undef UBSAN_CHECK |
| 94 | } |
| 95 | UNREACHABLE("unknown ErrorType!" ); |
| 96 | } |
| 97 | |
| 98 | static void MaybeReportErrorSummary(Location Loc, ErrorType Type) { |
| 99 | if (!common_flags()->print_summary) |
| 100 | return; |
| 101 | if (!flags()->report_error_type) |
| 102 | Type = ErrorType::GenericUB; |
| 103 | const char *ErrorKind = ConvertTypeToString(Type); |
| 104 | if (Loc.isSourceLocation()) { |
| 105 | SourceLocation SLoc = Loc.getSourceLocation(); |
| 106 | if (!SLoc.isInvalid()) { |
| 107 | AddressInfo AI; |
| 108 | AI.file = internal_strdup(s: SLoc.getFilename()); |
| 109 | AI.line = SLoc.getLine(); |
| 110 | AI.column = SLoc.getColumn(); |
| 111 | AI.function = nullptr; |
| 112 | ReportErrorSummary(error_type: ErrorKind, info: AI, alt_tool_name: GetSanititizerToolName()); |
| 113 | AI.Clear(); |
| 114 | return; |
| 115 | } |
| 116 | } else if (Loc.isSymbolizedStack()) { |
| 117 | const AddressInfo &AI = Loc.getSymbolizedStack()->info; |
| 118 | ReportErrorSummary(error_type: ErrorKind, info: AI, alt_tool_name: GetSanititizerToolName()); |
| 119 | return; |
| 120 | } |
| 121 | ReportErrorSummary(error_message: ErrorKind, alt_tool_name: GetSanititizerToolName()); |
| 122 | } |
| 123 | |
| 124 | namespace { |
| 125 | class Decorator : public SanitizerCommonDecorator { |
| 126 | public: |
| 127 | Decorator() : SanitizerCommonDecorator() {} |
| 128 | const char *Highlight() const { return Green(); } |
| 129 | const char *Note() const { return Black(); } |
| 130 | }; |
| 131 | } // namespace |
| 132 | |
| 133 | // Symbolization hook for the remote device support. The hook returns a null |
| 134 | // pointer if the given PC was not present in any of the known device images. |
| 135 | static SymbolizedStack *(*OffloadSymbolize)(uptr PC); |
| 136 | extern "C" SANITIZER_INTERFACE_ATTRIBUTE void |
| 137 | __ubsan_set_offload_symbolize(SymbolizedStack *(*Fn)(uptr PC)) { |
| 138 | OffloadSymbolize = Fn; |
| 139 | } |
| 140 | |
| 141 | SymbolizedStack *__ubsan::getSymbolizedLocation(uptr PC) { |
| 142 | InitAsStandaloneIfNecessary(); |
| 143 | if (OffloadSymbolize) |
| 144 | if (SymbolizedStack *S = OffloadSymbolize(PC)) |
| 145 | return S; |
| 146 | return Symbolizer::GetOrInit()->SymbolizePC(address: PC); |
| 147 | } |
| 148 | |
| 149 | Diag &Diag::operator<<(const TypeDescriptor &V) { |
| 150 | return AddArg(A: V.getTypeName()); |
| 151 | } |
| 152 | |
| 153 | Diag &Diag::operator<<(const Value &V) { |
| 154 | if (V.getType().isSignedIntegerTy()) |
| 155 | AddArg(A: V.getSIntValue()); |
| 156 | else if (V.getType().isUnsignedIntegerTy()) |
| 157 | AddArg(A: V.getUIntValue()); |
| 158 | else if (V.getType().isFloatTy()) |
| 159 | AddArg(A: V.getFloatValue()); |
| 160 | else |
| 161 | AddArg(A: "<unknown>" ); |
| 162 | return *this; |
| 163 | } |
| 164 | |
| 165 | /// Hexadecimal printing for numbers too large for Printf to handle directly. |
| 166 | static void RenderHex(InternalScopedString *Buffer, UIntMax Val) { |
| 167 | #if HAVE_INT128_T |
| 168 | Buffer->AppendF(format: "0x%08x%08x%08x%08x" , (unsigned int)(Val >> 96), |
| 169 | (unsigned int)(Val >> 64), (unsigned int)(Val >> 32), |
| 170 | (unsigned int)(Val)); |
| 171 | #else |
| 172 | UNREACHABLE("long long smaller than 64 bits?" ); |
| 173 | #endif |
| 174 | } |
| 175 | |
| 176 | static void RenderLocation(InternalScopedString *Buffer, Location Loc) { |
| 177 | switch (Loc.getKind()) { |
| 178 | case Location::LK_Source: { |
| 179 | SourceLocation SLoc = Loc.getSourceLocation(); |
| 180 | if (SLoc.isInvalid()) |
| 181 | Buffer->AppendF(format: "<unknown>" ); |
| 182 | else |
| 183 | StackTracePrinter::GetOrInit()->RenderSourceLocation( |
| 184 | buffer: Buffer, file: SLoc.getFilename(), line: SLoc.getLine(), column: SLoc.getColumn(), |
| 185 | vs_style: common_flags()->symbolize_vs_style, |
| 186 | strip_path_prefix: common_flags()->strip_path_prefix); |
| 187 | return; |
| 188 | } |
| 189 | case Location::LK_Memory: |
| 190 | Buffer->AppendF(format: "%p" , reinterpret_cast<void *>(Loc.getMemoryLocation())); |
| 191 | return; |
| 192 | case Location::LK_Symbolized: { |
| 193 | const AddressInfo &Info = Loc.getSymbolizedStack()->info; |
| 194 | if (Info.file) |
| 195 | StackTracePrinter::GetOrInit()->RenderSourceLocation( |
| 196 | buffer: Buffer, file: Info.file, line: Info.line, column: Info.column, |
| 197 | vs_style: common_flags()->symbolize_vs_style, |
| 198 | strip_path_prefix: common_flags()->strip_path_prefix); |
| 199 | else if (Info.module) |
| 200 | StackTracePrinter::GetOrInit()->RenderModuleLocation( |
| 201 | buffer: Buffer, module: Info.module, offset: Info.module_offset, arch: Info.module_arch, |
| 202 | strip_path_prefix: common_flags()->strip_path_prefix); |
| 203 | else |
| 204 | Buffer->AppendF(format: "%p" , reinterpret_cast<void *>(Info.address)); |
| 205 | return; |
| 206 | } |
| 207 | case Location::LK_Null: |
| 208 | Buffer->AppendF(format: "<unknown>" ); |
| 209 | return; |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | static void RenderText(InternalScopedString *Buffer, const char *Message, |
| 214 | const Diag::Arg *Args) { |
| 215 | for (const char *Msg = Message; *Msg; ++Msg) { |
| 216 | if (*Msg != '%') { |
| 217 | Buffer->AppendF(format: "%c" , *Msg); |
| 218 | continue; |
| 219 | } |
| 220 | const Diag::Arg &A = Args[*++Msg - '0']; |
| 221 | switch (A.Kind) { |
| 222 | case Diag::AK_String: |
| 223 | Buffer->AppendF(format: "%s" , A.String); |
| 224 | break; |
| 225 | case Diag::AK_TypeName: { |
| 226 | if (SANITIZER_WINDOWS) |
| 227 | // The Windows implementation demangles names early. |
| 228 | Buffer->AppendF(format: "'%s'" , A.String); |
| 229 | else |
| 230 | Buffer->AppendF(format: "'%s'" , Symbolizer::GetOrInit()->Demangle(name: A.String)); |
| 231 | break; |
| 232 | } |
| 233 | case Diag::AK_SInt: |
| 234 | // 'long long' is guaranteed to be at least 64 bits wide. |
| 235 | if (A.SInt >= INT64_MIN && A.SInt <= INT64_MAX) |
| 236 | Buffer->AppendF(format: "%lld" , (long long)A.SInt); |
| 237 | else |
| 238 | RenderHex(Buffer, Val: A.SInt); |
| 239 | break; |
| 240 | case Diag::AK_UInt: |
| 241 | if (A.UInt <= UINT64_MAX) |
| 242 | Buffer->AppendF(format: "%llu" , (unsigned long long)A.UInt); |
| 243 | else |
| 244 | RenderHex(Buffer, Val: A.UInt); |
| 245 | break; |
| 246 | case Diag::AK_Float: { |
| 247 | // FIXME: Support floating-point formatting in sanitizer_common's |
| 248 | // printf, and stop using snprintf here. |
| 249 | char FloatBuffer[32]; |
| 250 | #if SANITIZER_WINDOWS |
| 251 | // On MSVC platforms, long doubles are equal to regular doubles. |
| 252 | // In MinGW environments on x86, long doubles are 80 bit, but here, |
| 253 | // we're calling an MS CRT provided printf function which considers |
| 254 | // long doubles to be 64 bit. Just cast the float value to a regular |
| 255 | // double to avoid the potential ambiguity in MinGW mode. |
| 256 | sprintf_s(FloatBuffer, sizeof(FloatBuffer), "%g" , (double)A.Float); |
| 257 | #else |
| 258 | snprintf(s: FloatBuffer, maxlen: sizeof(FloatBuffer), format: "%Lg" , (long double)A.Float); |
| 259 | #endif |
| 260 | Buffer->Append(str: FloatBuffer); |
| 261 | break; |
| 262 | } |
| 263 | case Diag::AK_Pointer: |
| 264 | Buffer->AppendF(format: "%p" , A.Pointer); |
| 265 | break; |
| 266 | } |
| 267 | } |
| 268 | } |
| 269 | |
| 270 | /// Find the earliest-starting range in Ranges which ends after Loc. |
| 271 | static Range *upperBound(MemoryLocation Loc, Range *Ranges, |
| 272 | unsigned NumRanges) { |
| 273 | Range *Best = 0; |
| 274 | for (unsigned I = 0; I != NumRanges; ++I) |
| 275 | if (Ranges[I].getEnd().getMemoryLocation() > Loc && |
| 276 | (!Best || |
| 277 | Best->getStart().getMemoryLocation() > |
| 278 | Ranges[I].getStart().getMemoryLocation())) |
| 279 | Best = &Ranges[I]; |
| 280 | return Best; |
| 281 | } |
| 282 | |
| 283 | static inline uptr subtractNoOverflow(uptr LHS, uptr RHS) { |
| 284 | return (LHS < RHS) ? 0 : LHS - RHS; |
| 285 | } |
| 286 | |
| 287 | static inline uptr addNoOverflow(uptr LHS, uptr RHS) { |
| 288 | const uptr Limit = (uptr)-1; |
| 289 | return (LHS > Limit - RHS) ? Limit : LHS + RHS; |
| 290 | } |
| 291 | |
| 292 | /// Render a snippet of the address space near a location. |
| 293 | static void PrintMemorySnippet(const Decorator &Decor, MemoryLocation Loc, |
| 294 | Range *Ranges, unsigned NumRanges, |
| 295 | const Diag::Arg *Args) { |
| 296 | // Show at least the 8 bytes surrounding Loc. |
| 297 | const unsigned MinBytesNearLoc = 4; |
| 298 | MemoryLocation Min = subtractNoOverflow(LHS: Loc, RHS: MinBytesNearLoc); |
| 299 | MemoryLocation Max = addNoOverflow(LHS: Loc, RHS: MinBytesNearLoc); |
| 300 | MemoryLocation OrigMin = Min; |
| 301 | for (unsigned I = 0; I < NumRanges; ++I) { |
| 302 | Min = __sanitizer::Min(a: Ranges[I].getStart().getMemoryLocation(), b: Min); |
| 303 | Max = __sanitizer::Max(a: Ranges[I].getEnd().getMemoryLocation(), b: Max); |
| 304 | } |
| 305 | |
| 306 | // If we have too many interesting bytes, prefer to show bytes after Loc. |
| 307 | const unsigned BytesToShow = 32; |
| 308 | if (Max - Min > BytesToShow) |
| 309 | Min = __sanitizer::Min(a: Max - BytesToShow, b: OrigMin); |
| 310 | Max = addNoOverflow(LHS: Min, RHS: BytesToShow); |
| 311 | |
| 312 | if (!IsAccessibleMemoryRange(beg: Min, size: Max - Min)) { |
| 313 | Printf(format: "<memory cannot be printed>\n" ); |
| 314 | return; |
| 315 | } |
| 316 | |
| 317 | // Emit data. |
| 318 | InternalScopedString Buffer; |
| 319 | for (uptr P = Min; P != Max; ++P) { |
| 320 | unsigned char C = *reinterpret_cast<const unsigned char *>(P); |
| 321 | Buffer.AppendF(format: "%s%02x" , (P % 8 == 0) ? " " : " " , C); |
| 322 | } |
| 323 | Buffer.AppendF(format: "\n" ); |
| 324 | |
| 325 | // Emit highlights. |
| 326 | Buffer.Append(str: Decor.Highlight()); |
| 327 | Range *InRange = upperBound(Loc: Min, Ranges, NumRanges); |
| 328 | for (uptr P = Min; P != Max; ++P) { |
| 329 | char Pad = ' ', Byte = ' '; |
| 330 | if (InRange && InRange->getEnd().getMemoryLocation() == P) |
| 331 | InRange = upperBound(Loc: P, Ranges, NumRanges); |
| 332 | if (!InRange && P > Loc) |
| 333 | break; |
| 334 | if (InRange && InRange->getStart().getMemoryLocation() < P) |
| 335 | Pad = '~'; |
| 336 | if (InRange && InRange->getStart().getMemoryLocation() <= P) |
| 337 | Byte = '~'; |
| 338 | if (P % 8 == 0) |
| 339 | Buffer.AppendF(format: "%c" , Pad); |
| 340 | Buffer.AppendF(format: "%c" , Pad); |
| 341 | Buffer.AppendF(format: "%c" , P == Loc ? '^' : Byte); |
| 342 | Buffer.AppendF(format: "%c" , Byte); |
| 343 | } |
| 344 | Buffer.AppendF(format: "%s\n" , Decor.Default()); |
| 345 | |
| 346 | // Go over the line again, and print names for the ranges. |
| 347 | InRange = 0; |
| 348 | unsigned Spaces = 0; |
| 349 | for (uptr P = Min; P != Max; ++P) { |
| 350 | if (!InRange || InRange->getEnd().getMemoryLocation() == P) |
| 351 | InRange = upperBound(Loc: P, Ranges, NumRanges); |
| 352 | if (!InRange) |
| 353 | break; |
| 354 | |
| 355 | Spaces += (P % 8) == 0 ? 2 : 1; |
| 356 | |
| 357 | if (InRange && InRange->getStart().getMemoryLocation() == P) { |
| 358 | while (Spaces--) |
| 359 | Buffer.AppendF(format: " " ); |
| 360 | RenderText(Buffer: &Buffer, Message: InRange->getText(), Args); |
| 361 | Buffer.AppendF(format: "\n" ); |
| 362 | // FIXME: We only support naming one range for now! |
| 363 | break; |
| 364 | } |
| 365 | |
| 366 | Spaces += 2; |
| 367 | } |
| 368 | |
| 369 | Printf(format: "%s" , Buffer.data()); |
| 370 | // FIXME: Print names for anything we can identify within the line: |
| 371 | // |
| 372 | // * If we can identify the memory itself as belonging to a particular |
| 373 | // global, stack variable, or dynamic allocation, then do so. |
| 374 | // |
| 375 | // * If we have a pointer-size, pointer-aligned range highlighted, |
| 376 | // determine whether the value of that range is a pointer to an |
| 377 | // entity which we can name, and if so, print that name. |
| 378 | // |
| 379 | // This needs an external symbolizer, or (preferably) ASan instrumentation. |
| 380 | } |
| 381 | |
| 382 | Diag::~Diag() { |
| 383 | // All diagnostics should be printed under report mutex. |
| 384 | ScopedReport::CheckLocked(); |
| 385 | Decorator Decor; |
| 386 | InternalScopedString Buffer; |
| 387 | |
| 388 | // Prepare a report that a monitor process can inspect. |
| 389 | if (Level == DL_Error) { |
| 390 | RenderText(Buffer: &Buffer, Message, Args); |
| 391 | UndefinedBehaviorReport UBR{ConvertTypeToString(Type: ET), Loc, Buffer}; |
| 392 | Buffer.clear(); |
| 393 | } |
| 394 | |
| 395 | Buffer.Append(str: Decor.Bold()); |
| 396 | RenderLocation(Buffer: &Buffer, Loc); |
| 397 | Buffer.AppendF(format: ":" ); |
| 398 | |
| 399 | switch (Level) { |
| 400 | case DL_Error: |
| 401 | Buffer.AppendF(format: "%s runtime error: %s%s" , Decor.Warning(), Decor.Default(), |
| 402 | Decor.Bold()); |
| 403 | break; |
| 404 | |
| 405 | case DL_Note: |
| 406 | Buffer.AppendF(format: "%s note: %s" , Decor.Note(), Decor.Default()); |
| 407 | break; |
| 408 | } |
| 409 | |
| 410 | RenderText(Buffer: &Buffer, Message, Args); |
| 411 | |
| 412 | Buffer.AppendF(format: "%s\n" , Decor.Default()); |
| 413 | Printf(format: "%s" , Buffer.data()); |
| 414 | |
| 415 | if (Loc.isMemoryLocation()) |
| 416 | PrintMemorySnippet(Decor, Loc: Loc.getMemoryLocation(), Ranges, NumRanges, Args); |
| 417 | } |
| 418 | |
| 419 | static void PrintSymbolizedFrames(SymbolizedStack *Frames) { |
| 420 | InternalScopedString Out; |
| 421 | uptr N = 0; |
| 422 | for (SymbolizedStack *F = Frames; F; F = F->next) { |
| 423 | uptr Was = Out.length(); |
| 424 | StackTracePrinter::GetOrInit()->RenderFrame( |
| 425 | buffer: &Out, format: common_flags()->stack_trace_format, frame_no: N++, address: F->info.address, |
| 426 | info: &F->info, vs_style: common_flags()->symbolize_vs_style, |
| 427 | strip_path_prefix: common_flags()->strip_path_prefix); |
| 428 | if (Out.length() != Was) |
| 429 | Out.Append(str: "\n" ); |
| 430 | } |
| 431 | Out.Append(str: "\n" ); |
| 432 | Printf(format: "%s" , Out.data()); |
| 433 | } |
| 434 | |
| 435 | ScopedReport::Initializer::Initializer() { InitAsStandaloneIfNecessary(); } |
| 436 | |
| 437 | ScopedReport::ScopedReport(ReportOptions Opts, Location SummaryLoc, |
| 438 | ErrorType Type) |
| 439 | : Opts(Opts), SummaryLoc(SummaryLoc), Type(Type) {} |
| 440 | |
| 441 | ScopedReport::~ScopedReport() { |
| 442 | MaybePrintStackTrace(Opts); |
| 443 | MaybeReportErrorSummary(Loc: SummaryLoc, Type); |
| 444 | |
| 445 | if (common_flags()->print_module_map >= 2) |
| 446 | DumpProcessMap(); |
| 447 | |
| 448 | if (flags()->halt_on_error) |
| 449 | Die(); |
| 450 | } |
| 451 | |
| 452 | alignas(64) static char suppression_placeholder[sizeof(SuppressionContext)]; |
| 453 | static SuppressionContext *suppression_ctx = nullptr; |
| 454 | static const char kVptrCheck[] = "vptr_check" ; |
| 455 | static const char *kSuppressionTypes[] = { |
| 456 | #define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) FSanitizeFlagName, |
| 457 | #include "ubsan_checks.inc" |
| 458 | #undef UBSAN_CHECK |
| 459 | kVptrCheck, |
| 460 | }; |
| 461 | |
| 462 | void __ubsan::InitializeSuppressions() { |
| 463 | CHECK_EQ(nullptr, suppression_ctx); |
| 464 | suppression_ctx = new (suppression_placeholder) |
| 465 | SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes)); |
| 466 | suppression_ctx->ParseFromFile(filename: flags()->suppressions); |
| 467 | suppression_ctx->Parse(str: __ubsan_default_suppressions()); |
| 468 | } |
| 469 | |
| 470 | bool __ubsan::IsVptrCheckSuppressed(const char *TypeName) { |
| 471 | InitAsStandaloneIfNecessary(); |
| 472 | CHECK(suppression_ctx); |
| 473 | Suppression *s; |
| 474 | return suppression_ctx->Match(str: TypeName, type: kVptrCheck, s: &s); |
| 475 | } |
| 476 | |
| 477 | bool __ubsan::IsPCSuppressed(ErrorType ET, uptr PC, const char *Filename) { |
| 478 | InitAsStandaloneIfNecessary(); |
| 479 | CHECK(suppression_ctx); |
| 480 | // PC is the return address from the UBSan handler call. Symbolize the |
| 481 | // instruction that caused the call. |
| 482 | PC = StackTrace::GetPreviousInstructionPc(pc: PC); |
| 483 | const char *SuppType = ConvertTypeToFlagName(Type: ET); |
| 484 | // Fast path: don't symbolize PC if there is no suppressions for given UB |
| 485 | // type. |
| 486 | if (!suppression_ctx->HasSuppressionType(type: SuppType)) |
| 487 | return false; |
| 488 | Suppression *s = nullptr; |
| 489 | // Suppress by file name known to runtime. |
| 490 | if (Filename != nullptr && suppression_ctx->Match(str: Filename, type: SuppType, s: &s)) |
| 491 | return true; |
| 492 | // Suppress by module name. |
| 493 | if (const char *Module = Symbolizer::GetOrInit()->GetModuleNameForPc(pc: PC)) { |
| 494 | if (suppression_ctx->Match(str: Module, type: SuppType, s: &s)) |
| 495 | return true; |
| 496 | } |
| 497 | // Suppress by function or source file name from debug info. |
| 498 | // The first frame is the innermost logical inline frame, if inline debug |
| 499 | // information is available. Do not search the rest of the chain: a |
| 500 | // suppression for an inline wrapper must not suppress an inlined callee. |
| 501 | SymbolizedStackHolder Stack(Symbolizer::GetOrInit()->SymbolizePC(address: PC)); |
| 502 | const AddressInfo &AI = Stack.get()->info; |
| 503 | return suppression_ctx->Match(str: AI.function, type: SuppType, s: &s) || |
| 504 | suppression_ctx->Match(str: AI.file, type: SuppType, s: &s); |
| 505 | } |
| 506 | |
| 507 | #endif // CAN_SANITIZE_UB |
| 508 | |