| //===-- ubsan_diag.h --------------------------------------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Diagnostics emission for Clang's undefined behavior sanitizer. |
| // |
| //===----------------------------------------------------------------------===// |
| #ifndef UBSAN_DIAG_H |
| #define UBSAN_DIAG_H |
| |
| #include "ubsan_value.h" |
| #include "sanitizer_common/sanitizer_stacktrace.h" |
| #include "sanitizer_common/sanitizer_symbolizer.h" |
| |
| namespace __ubsan { |
| |
| class SymbolizedStackHolder { |
| SymbolizedStack *Stack; |
| |
| void clear() { |
| if (Stack) |
| Stack->ClearAll(); |
| } |
| |
| public: |
| explicit SymbolizedStackHolder(SymbolizedStack *Stack = nullptr) |
| : Stack(Stack) {} |
| ~SymbolizedStackHolder() { clear(); } |
| void reset(SymbolizedStack *S) { |
| if (Stack != S) |
| clear(); |
| Stack = S; |
| } |
| const SymbolizedStack *get() const { return Stack; } |
| }; |
| |
| SymbolizedStack *getSymbolizedLocation(uptr PC); |
| |
| inline SymbolizedStack *getCallerLocation(uptr CallerPC) { |
| CHECK(CallerPC); |
| uptr PC = StackTrace::GetPreviousInstructionPc(CallerPC); |
| return getSymbolizedLocation(PC); |
| } |
| |
| /// A location of some data within the program's address space. |
| typedef uptr MemoryLocation; |
| |
| /// \brief Location at which a diagnostic can be emitted. Either a |
| /// SourceLocation, a MemoryLocation, or a SymbolizedStack. |
| class Location { |
| public: |
| enum LocationKind { LK_Null, LK_Source, LK_Memory, LK_Symbolized }; |
| |
| private: |
| LocationKind Kind; |
| // FIXME: In C++11, wrap these in an anonymous union. |
| SourceLocation SourceLoc; |
| MemoryLocation MemoryLoc; |
| const SymbolizedStack *SymbolizedLoc; // Not owned. |
| |
| public: |
| Location() : Kind(LK_Null) {} |
| Location(SourceLocation Loc) : |
| Kind(LK_Source), SourceLoc(Loc) {} |
| Location(MemoryLocation Loc) : |
| Kind(LK_Memory), MemoryLoc(Loc) {} |
| // SymbolizedStackHolder must outlive Location object. |
| Location(const SymbolizedStackHolder &Stack) : |
| Kind(LK_Symbolized), SymbolizedLoc(Stack.get()) {} |
| |
| LocationKind getKind() const { return Kind; } |
| |
| bool isSourceLocation() const { return Kind == LK_Source; } |
| bool isMemoryLocation() const { return Kind == LK_Memory; } |
| bool isSymbolizedStack() const { return Kind == LK_Symbolized; } |
| |
| SourceLocation getSourceLocation() const { |
| CHECK(isSourceLocation()); |
| return SourceLoc; |
| } |
| MemoryLocation getMemoryLocation() const { |
| CHECK(isMemoryLocation()); |
| return MemoryLoc; |
| } |
| const SymbolizedStack *getSymbolizedStack() const { |
| CHECK(isSymbolizedStack()); |
| return SymbolizedLoc; |
| } |
| }; |
| |
| /// A diagnostic severity level. |
| enum DiagLevel { |
| DL_Error, ///< An error. |
| DL_Note ///< A note, attached to a prior diagnostic. |
| }; |
| |
| /// \brief Annotation for a range of locations in a diagnostic. |
| class Range { |
| Location Start, End; |
| const char *Text; |
| |
| public: |
| Range() : Start(), End(), Text() {} |
| Range(MemoryLocation Start, MemoryLocation End, const char *Text) |
| : Start(Start), End(End), Text(Text) {} |
| Location getStart() const { return Start; } |
| Location getEnd() const { return End; } |
| const char *getText() const { return Text; } |
| }; |
| |
| /// \brief A C++ type name. Really just a strong typedef for 'const char*'. |
| class TypeName { |
| const char *Name; |
| public: |
| TypeName(const char *Name) : Name(Name) {} |
| const char *getName() const { return Name; } |
| }; |
| |
| enum class ErrorType { |
| #define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) Name, |
| #include "ubsan_checks.inc" |
| #undef UBSAN_CHECK |
| }; |
| |
| /// \brief Representation of an in-flight diagnostic. |
| /// |
| /// Temporary \c Diag instances are created by the handler routines to |
| /// accumulate arguments for a diagnostic. The destructor emits the diagnostic |
| /// message. |
| class Diag { |
| /// The location at which the problem occurred. |
| Location Loc; |
| |
| /// The diagnostic level. |
| DiagLevel Level; |
| |
| /// The error type. |
| ErrorType ET; |
| |
| /// The message which will be emitted, with %0, %1, ... placeholders for |
| /// arguments. |
| const char *Message; |
| |
| public: |
| /// Kinds of arguments, corresponding to members of \c Arg's union. |
| enum ArgKind { |
| AK_String, ///< A string argument, displayed as-is. |
| AK_TypeName,///< A C++ type name, possibly demangled before display. |
| AK_UInt, ///< An unsigned integer argument. |
| AK_SInt, ///< A signed integer argument. |
| AK_Float, ///< A floating-point argument. |
| AK_Pointer ///< A pointer argument, displayed in hexadecimal. |
| }; |
| |
| /// An individual diagnostic message argument. |
| struct Arg { |
| Arg() {} |
| Arg(const char *String) : Kind(AK_String), String(String) {} |
| Arg(TypeName TN) : Kind(AK_TypeName), String(TN.getName()) {} |
| Arg(UIntMax UInt) : Kind(AK_UInt), UInt(UInt) {} |
| Arg(SIntMax SInt) : Kind(AK_SInt), SInt(SInt) {} |
| Arg(FloatMax Float) : Kind(AK_Float), Float(Float) {} |
| Arg(const void *Pointer) : Kind(AK_Pointer), Pointer(Pointer) {} |
| |
| ArgKind Kind; |
| union { |
| const char *String; |
| UIntMax UInt; |
| SIntMax SInt; |
| FloatMax Float; |
| const void *Pointer; |
| }; |
| }; |
| |
| private: |
| static const unsigned MaxArgs = 8; |
| static const unsigned MaxRanges = 1; |
| |
| /// The arguments which have been added to this diagnostic so far. |
| Arg Args[MaxArgs]; |
| unsigned NumArgs; |
| |
| /// The ranges which have been added to this diagnostic so far. |
| Range Ranges[MaxRanges]; |
| unsigned NumRanges; |
| |
| Diag &AddArg(Arg A) { |
| CHECK(NumArgs != MaxArgs); |
| Args[NumArgs++] = A; |
| return *this; |
| } |
| |
| Diag &AddRange(Range A) { |
| CHECK(NumRanges != MaxRanges); |
| Ranges[NumRanges++] = A; |
| return *this; |
| } |
| |
| /// \c Diag objects are not copyable. |
| Diag(const Diag &); // NOT IMPLEMENTED |
| Diag &operator=(const Diag &); |
| |
| public: |
| Diag(Location Loc, DiagLevel Level, ErrorType ET, const char *Message) |
| : Loc(Loc), Level(Level), ET(ET), Message(Message), NumArgs(0), |
| NumRanges(0) {} |
| ~Diag(); |
| |
| Diag &operator<<(const char *Str) { return AddArg(Str); } |
| Diag &operator<<(TypeName TN) { return AddArg(TN); } |
| Diag &operator<<(unsigned long long V) { return AddArg(UIntMax(V)); } |
| Diag &operator<<(const void *V) { return AddArg(V); } |
| Diag &operator<<(const TypeDescriptor &V); |
| Diag &operator<<(const Value &V); |
| Diag &operator<<(const Range &R) { return AddRange(R); } |
| }; |
| |
| struct ReportOptions { |
| // If FromUnrecoverableHandler is specified, UBSan runtime handler is not |
| // expected to return. |
| bool FromUnrecoverableHandler; |
| /// pc/bp are used to unwind the stack trace. |
| uptr pc; |
| uptr bp; |
| }; |
| |
| bool ignoreReport(SourceLocation SLoc, ReportOptions Opts, ErrorType ET); |
| |
| #define GET_REPORT_OPTIONS(unrecoverable_handler) \ |
| GET_CALLER_PC_BP; \ |
| ReportOptions Opts = {unrecoverable_handler, pc, bp} |
| |
| /// \brief Instantiate this class before printing diagnostics in the error |
| /// report. This class ensures that reports from different threads and from |
| /// different sanitizers won't be mixed. |
| class ScopedReport { |
| struct Initializer { |
| Initializer(); |
| }; |
| Initializer initializer_; |
| ScopedErrorReportLock report_lock_; |
| |
| ReportOptions Opts; |
| Location SummaryLoc; |
| ErrorType Type; |
| |
| public: |
| ScopedReport(ReportOptions Opts, Location SummaryLoc, ErrorType Type); |
| ~ScopedReport(); |
| |
| static void CheckLocked() { ScopedErrorReportLock::CheckLocked(); } |
| }; |
| |
| void InitializeSuppressions(); |
| bool IsVptrCheckSuppressed(const char *TypeName); |
| // Sometimes UBSan runtime can know filename from handlers arguments, even if |
| // debug info is missing. |
| bool IsPCSuppressed(ErrorType ET, uptr PC, const char *Filename); |
| |
| } // namespace __ubsan |
| |
| #endif // UBSAN_DIAG_H |