xref: /freebsd/contrib/llvm-project/compiler-rt/lib/ubsan/ubsan_diag.h (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
1 //===-- ubsan_diag.h --------------------------------------------*- 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 // Diagnostics emission for Clang's undefined behavior sanitizer.
10 //
11 //===----------------------------------------------------------------------===//
12 #ifndef UBSAN_DIAG_H
13 #define UBSAN_DIAG_H
14 
15 #include "ubsan_value.h"
16 #include "sanitizer_common/sanitizer_stacktrace.h"
17 #include "sanitizer_common/sanitizer_symbolizer.h"
18 
19 namespace __ubsan {
20 
21 SymbolizedStack *getSymbolizedLocation(uptr PC);
22 
23 inline SymbolizedStack *getCallerLocation(uptr CallerPC) {
24   CHECK(CallerPC);
25   uptr PC = StackTrace::GetPreviousInstructionPc(CallerPC);
26   return getSymbolizedLocation(PC);
27 }
28 
29 /// A location of some data within the program's address space.
30 typedef uptr MemoryLocation;
31 
32 /// \brief Location at which a diagnostic can be emitted. Either a
33 /// SourceLocation, a MemoryLocation, or a SymbolizedStack.
34 class Location {
35 public:
36   enum LocationKind { LK_Null, LK_Source, LK_Memory, LK_Symbolized };
37 
38 private:
39   LocationKind Kind;
40   // FIXME: In C++11, wrap these in an anonymous union.
41   SourceLocation SourceLoc;
42   MemoryLocation MemoryLoc;
43   const SymbolizedStack *SymbolizedLoc;  // Not owned.
44 
45 public:
46   Location() : Kind(LK_Null) {}
47   Location(SourceLocation Loc) :
48     Kind(LK_Source), SourceLoc(Loc) {}
49   Location(MemoryLocation Loc) :
50     Kind(LK_Memory), MemoryLoc(Loc) {}
51   // SymbolizedStackHolder must outlive Location object.
52   Location(const SymbolizedStackHolder &Stack) :
53     Kind(LK_Symbolized), SymbolizedLoc(Stack.get()) {}
54 
55   LocationKind getKind() const { return Kind; }
56 
57   bool isSourceLocation() const { return Kind == LK_Source; }
58   bool isMemoryLocation() const { return Kind == LK_Memory; }
59   bool isSymbolizedStack() const { return Kind == LK_Symbolized; }
60 
61   SourceLocation getSourceLocation() const {
62     CHECK(isSourceLocation());
63     return SourceLoc;
64   }
65   MemoryLocation getMemoryLocation() const {
66     CHECK(isMemoryLocation());
67     return MemoryLoc;
68   }
69   const SymbolizedStack *getSymbolizedStack() const {
70     CHECK(isSymbolizedStack());
71     return SymbolizedLoc;
72   }
73 };
74 
75 /// A diagnostic severity level.
76 enum DiagLevel {
77   DL_Error, ///< An error.
78   DL_Note   ///< A note, attached to a prior diagnostic.
79 };
80 
81 /// \brief Annotation for a range of locations in a diagnostic.
82 class Range {
83   Location Start, End;
84   const char *Text;
85 
86 public:
87   Range() : Start(), End(), Text() {}
88   Range(MemoryLocation Start, MemoryLocation End, const char *Text)
89     : Start(Start), End(End), Text(Text) {}
90   Location getStart() const { return Start; }
91   Location getEnd() const { return End; }
92   const char *getText() const { return Text; }
93 };
94 
95 /// \brief A C++ type name. Really just a strong typedef for 'const char*'.
96 class TypeName {
97   const char *Name;
98 public:
99   TypeName(const char *Name) : Name(Name) {}
100   const char *getName() const { return Name; }
101 };
102 
103 enum class ErrorType {
104 #define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) Name,
105 #include "ubsan_checks.inc"
106 #undef UBSAN_CHECK
107 };
108 
109 /// \brief Representation of an in-flight diagnostic.
110 ///
111 /// Temporary \c Diag instances are created by the handler routines to
112 /// accumulate arguments for a diagnostic. The destructor emits the diagnostic
113 /// message.
114 class Diag {
115   /// The location at which the problem occurred.
116   Location Loc;
117 
118   /// The diagnostic level.
119   DiagLevel Level;
120 
121   /// The error type.
122   ErrorType ET;
123 
124   /// The message which will be emitted, with %0, %1, ... placeholders for
125   /// arguments.
126   const char *Message;
127 
128 public:
129   /// Kinds of arguments, corresponding to members of \c Arg's union.
130   enum ArgKind {
131     AK_String, ///< A string argument, displayed as-is.
132     AK_TypeName,///< A C++ type name, possibly demangled before display.
133     AK_UInt,   ///< An unsigned integer argument.
134     AK_SInt,   ///< A signed integer argument.
135     AK_Float,  ///< A floating-point argument.
136     AK_Pointer ///< A pointer argument, displayed in hexadecimal.
137   };
138 
139   /// An individual diagnostic message argument.
140   struct Arg {
141     Arg() {}
142     Arg(const char *String) : Kind(AK_String), String(String) {}
143     Arg(TypeName TN) : Kind(AK_TypeName), String(TN.getName()) {}
144     Arg(UIntMax UInt) : Kind(AK_UInt), UInt(UInt) {}
145     Arg(SIntMax SInt) : Kind(AK_SInt), SInt(SInt) {}
146     Arg(FloatMax Float) : Kind(AK_Float), Float(Float) {}
147     Arg(const void *Pointer) : Kind(AK_Pointer), Pointer(Pointer) {}
148 
149     ArgKind Kind;
150     union {
151       const char *String;
152       UIntMax UInt;
153       SIntMax SInt;
154       FloatMax Float;
155       const void *Pointer;
156     };
157   };
158 
159 private:
160   static const unsigned MaxArgs = 8;
161   static const unsigned MaxRanges = 1;
162 
163   /// The arguments which have been added to this diagnostic so far.
164   Arg Args[MaxArgs];
165   unsigned NumArgs;
166 
167   /// The ranges which have been added to this diagnostic so far.
168   Range Ranges[MaxRanges];
169   unsigned NumRanges;
170 
171   Diag &AddArg(Arg A) {
172     CHECK(NumArgs != MaxArgs);
173     Args[NumArgs++] = A;
174     return *this;
175   }
176 
177   Diag &AddRange(Range A) {
178     CHECK(NumRanges != MaxRanges);
179     Ranges[NumRanges++] = A;
180     return *this;
181   }
182 
183   /// \c Diag objects are not copyable.
184   Diag(const Diag &); // NOT IMPLEMENTED
185   Diag &operator=(const Diag &);
186 
187 public:
188   Diag(Location Loc, DiagLevel Level, ErrorType ET, const char *Message)
189       : Loc(Loc), Level(Level), ET(ET), Message(Message), NumArgs(0),
190         NumRanges(0) {}
191   ~Diag();
192 
193   Diag &operator<<(const char *Str) { return AddArg(Str); }
194   Diag &operator<<(TypeName TN) { return AddArg(TN); }
195   Diag &operator<<(unsigned long long V) { return AddArg(UIntMax(V)); }
196   Diag &operator<<(const void *V) { return AddArg(V); }
197   Diag &operator<<(const TypeDescriptor &V);
198   Diag &operator<<(const Value &V);
199   Diag &operator<<(const Range &R) { return AddRange(R); }
200 };
201 
202 struct ReportOptions {
203   // If FromUnrecoverableHandler is specified, UBSan runtime handler is not
204   // expected to return.
205   bool FromUnrecoverableHandler;
206   /// pc/bp are used to unwind the stack trace.
207   uptr pc;
208   uptr bp;
209 };
210 
211 bool ignoreReport(SourceLocation SLoc, ReportOptions Opts, ErrorType ET);
212 
213 #define GET_REPORT_OPTIONS(unrecoverable_handler) \
214     GET_CALLER_PC_BP; \
215     ReportOptions Opts = {unrecoverable_handler, pc, bp}
216 
217 /// \brief Instantiate this class before printing diagnostics in the error
218 /// report. This class ensures that reports from different threads and from
219 /// different sanitizers won't be mixed.
220 class ScopedReport {
221   struct Initializer {
222     Initializer();
223   };
224   Initializer initializer_;
225   ScopedErrorReportLock report_lock_;
226 
227   ReportOptions Opts;
228   Location SummaryLoc;
229   ErrorType Type;
230 
231 public:
232   ScopedReport(ReportOptions Opts, Location SummaryLoc, ErrorType Type);
233   ~ScopedReport();
234 
235   static void CheckLocked() { ScopedErrorReportLock::CheckLocked(); }
236 };
237 
238 void InitializeSuppressions();
239 bool IsVptrCheckSuppressed(const char *TypeName);
240 // Sometimes UBSan runtime can know filename from handlers arguments, even if
241 // debug info is missing.
242 bool IsPCSuppressed(ErrorType ET, uptr PC, const char *Filename);
243 
244 } // namespace __ubsan
245 
246 #endif // UBSAN_DIAG_H
247