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