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