1 | //===-- ubsan_handlers.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 | // Error logging entry points for the UBSan runtime. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "ubsan_platform.h" |
14 | #if CAN_SANITIZE_UB |
15 | #include "ubsan_handlers.h" |
16 | #include "ubsan_diag.h" |
17 | #include "ubsan_flags.h" |
18 | #include "ubsan_monitor.h" |
19 | #include "ubsan_value.h" |
20 | |
21 | #include "sanitizer_common/sanitizer_common.h" |
22 | |
23 | using namespace __sanitizer; |
24 | using namespace __ubsan; |
25 | |
26 | namespace __ubsan { |
27 | bool ignoreReport(SourceLocation SLoc, ReportOptions Opts, ErrorType ET) { |
28 | // We are not allowed to skip error report: if we are in unrecoverable |
29 | // handler, we have to terminate the program right now, and therefore |
30 | // have to print some diagnostic. |
31 | // |
32 | // Even if source location is disabled, it doesn't mean that we have |
33 | // already report an error to the user: some concurrently running |
34 | // thread could have acquired it, but not yet printed the report. |
35 | if (Opts.FromUnrecoverableHandler) |
36 | return false; |
37 | return SLoc.isDisabled() || IsPCSuppressed(ET, PC: Opts.pc, Filename: SLoc.getFilename()); |
38 | } |
39 | |
40 | /// Situations in which we might emit a check for the suitability of a |
41 | /// pointer or glvalue. Needs to be kept in sync with CodeGenFunction.h in |
42 | /// clang. |
43 | enum TypeCheckKind { |
44 | /// Checking the operand of a load. Must be suitably sized and aligned. |
45 | TCK_Load, |
46 | /// Checking the destination of a store. Must be suitably sized and aligned. |
47 | TCK_Store, |
48 | /// Checking the bound value in a reference binding. Must be suitably sized |
49 | /// and aligned, but is not required to refer to an object (until the |
50 | /// reference is used), per core issue 453. |
51 | TCK_ReferenceBinding, |
52 | /// Checking the object expression in a non-static data member access. Must |
53 | /// be an object within its lifetime. |
54 | TCK_MemberAccess, |
55 | /// Checking the 'this' pointer for a call to a non-static member function. |
56 | /// Must be an object within its lifetime. |
57 | TCK_MemberCall, |
58 | /// Checking the 'this' pointer for a constructor call. |
59 | TCK_ConstructorCall, |
60 | /// Checking the operand of a static_cast to a derived pointer type. Must be |
61 | /// null or an object within its lifetime. |
62 | TCK_DowncastPointer, |
63 | /// Checking the operand of a static_cast to a derived reference type. Must |
64 | /// be an object within its lifetime. |
65 | TCK_DowncastReference, |
66 | /// Checking the operand of a cast to a base object. Must be suitably sized |
67 | /// and aligned. |
68 | TCK_Upcast, |
69 | /// Checking the operand of a cast to a virtual base object. Must be an |
70 | /// object within its lifetime. |
71 | TCK_UpcastToVirtualBase, |
72 | /// Checking the value assigned to a _Nonnull pointer. Must not be null. |
73 | TCK_NonnullAssign, |
74 | /// Checking the operand of a dynamic_cast or a typeid expression. Must be |
75 | /// null or an object within its lifetime. |
76 | TCK_DynamicOperation |
77 | }; |
78 | |
79 | extern const char *const TypeCheckKinds[] = { |
80 | "load of" , "store to" , "reference binding to" , "member access within" , |
81 | "member call on" , "constructor call on" , "downcast of" , "downcast of" , |
82 | "upcast of" , "cast to virtual base of" , "_Nonnull binding to" , |
83 | "dynamic operation on" }; |
84 | } |
85 | |
86 | static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer, |
87 | ReportOptions Opts) { |
88 | Location Loc = Data->Loc.acquire(); |
89 | |
90 | uptr Alignment = (uptr)1 << Data->LogAlignment; |
91 | ErrorType ET; |
92 | if (!Pointer) |
93 | ET = (Data->TypeCheckKind == TCK_NonnullAssign) |
94 | ? ErrorType::NullPointerUseWithNullability |
95 | : ErrorType::NullPointerUse; |
96 | else if (Pointer & (Alignment - 1)) |
97 | ET = ErrorType::MisalignedPointerUse; |
98 | else |
99 | ET = ErrorType::InsufficientObjectSize; |
100 | |
101 | // Use the SourceLocation from Data to track deduplication, even if it's |
102 | // invalid. |
103 | if (ignoreReport(SLoc: Loc.getSourceLocation(), Opts, ET)) |
104 | return; |
105 | |
106 | SymbolizedStackHolder FallbackLoc; |
107 | if (Data->Loc.isInvalid()) { |
108 | FallbackLoc.reset(S: getCallerLocation(CallerPC: Opts.pc)); |
109 | Loc = FallbackLoc; |
110 | } |
111 | |
112 | ScopedReport R(Opts, Loc, ET); |
113 | |
114 | switch (ET) { |
115 | case ErrorType::NullPointerUse: |
116 | case ErrorType::NullPointerUseWithNullability: |
117 | Diag(Loc, DL_Error, ET, "%0 null pointer of type %1" ) |
118 | << TypeCheckKinds[Data->TypeCheckKind] << Data->Type; |
119 | break; |
120 | case ErrorType::MisalignedPointerUse: |
121 | Diag(Loc, DL_Error, ET, "%0 misaligned address %1 for type %3, " |
122 | "which requires %2 byte alignment" ) |
123 | << TypeCheckKinds[Data->TypeCheckKind] << (void *)Pointer << Alignment |
124 | << Data->Type; |
125 | break; |
126 | case ErrorType::InsufficientObjectSize: |
127 | Diag(Loc, DL_Error, ET, "%0 address %1 with insufficient space " |
128 | "for an object of type %2" ) |
129 | << TypeCheckKinds[Data->TypeCheckKind] << (void *)Pointer << Data->Type; |
130 | break; |
131 | default: |
132 | UNREACHABLE("unexpected error type!" ); |
133 | } |
134 | |
135 | if (Pointer) |
136 | Diag(Pointer, DL_Note, ET, "pointer points here" ); |
137 | } |
138 | |
139 | void __ubsan::__ubsan_handle_type_mismatch_v1(TypeMismatchData *Data, |
140 | ValueHandle Pointer) { |
141 | GET_REPORT_OPTIONS(false); |
142 | handleTypeMismatchImpl(Data, Pointer, Opts); |
143 | } |
144 | void __ubsan::__ubsan_handle_type_mismatch_v1_abort(TypeMismatchData *Data, |
145 | ValueHandle Pointer) { |
146 | GET_REPORT_OPTIONS(true); |
147 | handleTypeMismatchImpl(Data, Pointer, Opts); |
148 | Die(); |
149 | } |
150 | |
151 | static void handleAlignmentAssumptionImpl(AlignmentAssumptionData *Data, |
152 | ValueHandle Pointer, |
153 | ValueHandle Alignment, |
154 | ValueHandle Offset, |
155 | ReportOptions Opts) { |
156 | Location Loc = Data->Loc.acquire(); |
157 | SourceLocation AssumptionLoc = Data->AssumptionLoc.acquire(); |
158 | |
159 | ErrorType ET = ErrorType::AlignmentAssumption; |
160 | |
161 | if (ignoreReport(SLoc: Loc.getSourceLocation(), Opts, ET)) |
162 | return; |
163 | |
164 | ScopedReport R(Opts, Loc, ET); |
165 | |
166 | uptr RealPointer = Pointer - Offset; |
167 | uptr LSB = LeastSignificantSetBitIndex(x: RealPointer); |
168 | uptr ActualAlignment = uptr(1) << LSB; |
169 | |
170 | uptr Mask = Alignment - 1; |
171 | uptr MisAlignmentOffset = RealPointer & Mask; |
172 | |
173 | if (!Offset) { |
174 | Diag(Loc, DL_Error, ET, |
175 | "assumption of %0 byte alignment for pointer of type %1 failed" ) |
176 | << Alignment << Data->Type; |
177 | } else { |
178 | Diag(Loc, DL_Error, ET, |
179 | "assumption of %0 byte alignment (with offset of %1 byte) for pointer " |
180 | "of type %2 failed" ) |
181 | << Alignment << Offset << Data->Type; |
182 | } |
183 | |
184 | if (!AssumptionLoc.isInvalid()) |
185 | Diag(AssumptionLoc, DL_Note, ET, "alignment assumption was specified here" ); |
186 | |
187 | Diag(RealPointer, DL_Note, ET, |
188 | "%0address is %1 aligned, misalignment offset is %2 bytes" ) |
189 | << (Offset ? "offset " : "" ) << ActualAlignment << MisAlignmentOffset; |
190 | } |
191 | |
192 | void __ubsan::__ubsan_handle_alignment_assumption(AlignmentAssumptionData *Data, |
193 | ValueHandle Pointer, |
194 | ValueHandle Alignment, |
195 | ValueHandle Offset) { |
196 | GET_REPORT_OPTIONS(false); |
197 | handleAlignmentAssumptionImpl(Data, Pointer, Alignment, Offset, Opts); |
198 | } |
199 | void __ubsan::__ubsan_handle_alignment_assumption_abort( |
200 | AlignmentAssumptionData *Data, ValueHandle Pointer, ValueHandle Alignment, |
201 | ValueHandle Offset) { |
202 | GET_REPORT_OPTIONS(true); |
203 | handleAlignmentAssumptionImpl(Data, Pointer, Alignment, Offset, Opts); |
204 | Die(); |
205 | } |
206 | |
207 | /// \brief Common diagnostic emission for various forms of integer overflow. |
208 | template <typename T> |
209 | static void handleIntegerOverflowImpl(OverflowData *Data, ValueHandle LHS, |
210 | const char *Operator, T RHS, |
211 | ReportOptions Opts) { |
212 | SourceLocation Loc = Data->Loc.acquire(); |
213 | bool IsSigned = Data->Type.isSignedIntegerTy(); |
214 | ErrorType ET = IsSigned ? ErrorType::SignedIntegerOverflow |
215 | : ErrorType::UnsignedIntegerOverflow; |
216 | |
217 | if (ignoreReport(SLoc: Loc, Opts, ET)) |
218 | return; |
219 | |
220 | // If this is an unsigned overflow in non-fatal mode, potentially ignore it. |
221 | if (!IsSigned && !Opts.FromUnrecoverableHandler && |
222 | flags()->silence_unsigned_overflow) |
223 | return; |
224 | |
225 | ScopedReport R(Opts, Loc, ET); |
226 | |
227 | Diag(Loc, DL_Error, ET, "%0 integer overflow: " |
228 | "%1 %2 %3 cannot be represented in type %4" ) |
229 | << (IsSigned ? "signed" : "unsigned" ) << Value(Data->Type, LHS) |
230 | << Operator << RHS << Data->Type; |
231 | } |
232 | |
233 | #define UBSAN_OVERFLOW_HANDLER(handler_name, op, unrecoverable) \ |
234 | void __ubsan::handler_name(OverflowData *Data, ValueHandle LHS, \ |
235 | ValueHandle RHS) { \ |
236 | GET_REPORT_OPTIONS(unrecoverable); \ |
237 | handleIntegerOverflowImpl(Data, LHS, op, Value(Data->Type, RHS), Opts); \ |
238 | if (unrecoverable) \ |
239 | Die(); \ |
240 | } |
241 | |
242 | UBSAN_OVERFLOW_HANDLER(__ubsan_handle_add_overflow, "+" , false) |
243 | UBSAN_OVERFLOW_HANDLER(__ubsan_handle_add_overflow_abort, "+" , true) |
244 | UBSAN_OVERFLOW_HANDLER(__ubsan_handle_sub_overflow, "-" , false) |
245 | UBSAN_OVERFLOW_HANDLER(__ubsan_handle_sub_overflow_abort, "-" , true) |
246 | UBSAN_OVERFLOW_HANDLER(__ubsan_handle_mul_overflow, "*" , false) |
247 | UBSAN_OVERFLOW_HANDLER(__ubsan_handle_mul_overflow_abort, "*" , true) |
248 | |
249 | static void handleNegateOverflowImpl(OverflowData *Data, ValueHandle OldVal, |
250 | ReportOptions Opts) { |
251 | SourceLocation Loc = Data->Loc.acquire(); |
252 | bool IsSigned = Data->Type.isSignedIntegerTy(); |
253 | ErrorType ET = IsSigned ? ErrorType::SignedIntegerOverflow |
254 | : ErrorType::UnsignedIntegerOverflow; |
255 | |
256 | if (ignoreReport(SLoc: Loc, Opts, ET)) |
257 | return; |
258 | |
259 | if (!IsSigned && flags()->silence_unsigned_overflow) |
260 | return; |
261 | |
262 | ScopedReport R(Opts, Loc, ET); |
263 | |
264 | if (IsSigned) |
265 | Diag(Loc, DL_Error, ET, |
266 | "negation of %0 cannot be represented in type %1; " |
267 | "cast to an unsigned type to negate this value to itself" ) |
268 | << Value(Data->Type, OldVal) << Data->Type; |
269 | else |
270 | Diag(Loc, DL_Error, ET, "negation of %0 cannot be represented in type %1" ) |
271 | << Value(Data->Type, OldVal) << Data->Type; |
272 | } |
273 | |
274 | void __ubsan::__ubsan_handle_negate_overflow(OverflowData *Data, |
275 | ValueHandle OldVal) { |
276 | GET_REPORT_OPTIONS(false); |
277 | handleNegateOverflowImpl(Data, OldVal, Opts); |
278 | } |
279 | void __ubsan::__ubsan_handle_negate_overflow_abort(OverflowData *Data, |
280 | ValueHandle OldVal) { |
281 | GET_REPORT_OPTIONS(true); |
282 | handleNegateOverflowImpl(Data, OldVal, Opts); |
283 | Die(); |
284 | } |
285 | |
286 | static void handleDivremOverflowImpl(OverflowData *Data, ValueHandle LHS, |
287 | ValueHandle RHS, ReportOptions Opts) { |
288 | SourceLocation Loc = Data->Loc.acquire(); |
289 | Value LHSVal(Data->Type, LHS); |
290 | Value RHSVal(Data->Type, RHS); |
291 | |
292 | ErrorType ET; |
293 | if (RHSVal.isMinusOne()) |
294 | ET = ErrorType::SignedIntegerOverflow; |
295 | else if (Data->Type.isIntegerTy()) |
296 | ET = ErrorType::IntegerDivideByZero; |
297 | else |
298 | ET = ErrorType::FloatDivideByZero; |
299 | |
300 | if (ignoreReport(SLoc: Loc, Opts, ET)) |
301 | return; |
302 | |
303 | ScopedReport R(Opts, Loc, ET); |
304 | |
305 | switch (ET) { |
306 | case ErrorType::SignedIntegerOverflow: |
307 | Diag(Loc, DL_Error, ET, |
308 | "division of %0 by -1 cannot be represented in type %1" ) |
309 | << LHSVal << Data->Type; |
310 | break; |
311 | default: |
312 | Diag(Loc, DL_Error, ET, "division by zero" ); |
313 | break; |
314 | } |
315 | } |
316 | |
317 | void __ubsan::__ubsan_handle_divrem_overflow(OverflowData *Data, |
318 | ValueHandle LHS, ValueHandle RHS) { |
319 | GET_REPORT_OPTIONS(false); |
320 | handleDivremOverflowImpl(Data, LHS, RHS, Opts); |
321 | } |
322 | void __ubsan::__ubsan_handle_divrem_overflow_abort(OverflowData *Data, |
323 | ValueHandle LHS, |
324 | ValueHandle RHS) { |
325 | GET_REPORT_OPTIONS(true); |
326 | handleDivremOverflowImpl(Data, LHS, RHS, Opts); |
327 | Die(); |
328 | } |
329 | |
330 | static void handleShiftOutOfBoundsImpl(ShiftOutOfBoundsData *Data, |
331 | ValueHandle LHS, ValueHandle RHS, |
332 | ReportOptions Opts) { |
333 | SourceLocation Loc = Data->Loc.acquire(); |
334 | Value LHSVal(Data->LHSType, LHS); |
335 | Value RHSVal(Data->RHSType, RHS); |
336 | |
337 | ErrorType ET; |
338 | if (RHSVal.isNegative() || |
339 | RHSVal.getPositiveIntValue() >= Data->LHSType.getIntegerBitWidth()) |
340 | ET = ErrorType::InvalidShiftExponent; |
341 | else |
342 | ET = ErrorType::InvalidShiftBase; |
343 | |
344 | if (ignoreReport(SLoc: Loc, Opts, ET)) |
345 | return; |
346 | |
347 | ScopedReport R(Opts, Loc, ET); |
348 | |
349 | if (ET == ErrorType::InvalidShiftExponent) { |
350 | if (RHSVal.isNegative()) |
351 | Diag(Loc, DL_Error, ET, "shift exponent %0 is negative" ) << RHSVal; |
352 | else |
353 | Diag(Loc, DL_Error, ET, |
354 | "shift exponent %0 is too large for %1-bit type %2" ) |
355 | << RHSVal << Data->LHSType.getIntegerBitWidth() << Data->LHSType; |
356 | } else { |
357 | if (LHSVal.isNegative()) |
358 | Diag(Loc, DL_Error, ET, "left shift of negative value %0" ) << LHSVal; |
359 | else |
360 | Diag(Loc, DL_Error, ET, |
361 | "left shift of %0 by %1 places cannot be represented in type %2" ) |
362 | << LHSVal << RHSVal << Data->LHSType; |
363 | } |
364 | } |
365 | |
366 | void __ubsan::__ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData *Data, |
367 | ValueHandle LHS, |
368 | ValueHandle RHS) { |
369 | GET_REPORT_OPTIONS(false); |
370 | handleShiftOutOfBoundsImpl(Data, LHS, RHS, Opts); |
371 | } |
372 | void __ubsan::__ubsan_handle_shift_out_of_bounds_abort( |
373 | ShiftOutOfBoundsData *Data, |
374 | ValueHandle LHS, |
375 | ValueHandle RHS) { |
376 | GET_REPORT_OPTIONS(true); |
377 | handleShiftOutOfBoundsImpl(Data, LHS, RHS, Opts); |
378 | Die(); |
379 | } |
380 | |
381 | static void handleOutOfBoundsImpl(OutOfBoundsData *Data, ValueHandle Index, |
382 | ReportOptions Opts) { |
383 | SourceLocation Loc = Data->Loc.acquire(); |
384 | ErrorType ET = ErrorType::OutOfBoundsIndex; |
385 | |
386 | if (ignoreReport(SLoc: Loc, Opts, ET)) |
387 | return; |
388 | |
389 | ScopedReport R(Opts, Loc, ET); |
390 | |
391 | Value IndexVal(Data->IndexType, Index); |
392 | Diag(Loc, DL_Error, ET, "index %0 out of bounds for type %1" ) |
393 | << IndexVal << Data->ArrayType; |
394 | } |
395 | |
396 | void __ubsan::__ubsan_handle_out_of_bounds(OutOfBoundsData *Data, |
397 | ValueHandle Index) { |
398 | GET_REPORT_OPTIONS(false); |
399 | handleOutOfBoundsImpl(Data, Index, Opts); |
400 | } |
401 | void __ubsan::__ubsan_handle_out_of_bounds_abort(OutOfBoundsData *Data, |
402 | ValueHandle Index) { |
403 | GET_REPORT_OPTIONS(true); |
404 | handleOutOfBoundsImpl(Data, Index, Opts); |
405 | Die(); |
406 | } |
407 | |
408 | static void handleBuiltinUnreachableImpl(UnreachableData *Data, |
409 | ReportOptions Opts) { |
410 | ErrorType ET = ErrorType::UnreachableCall; |
411 | ScopedReport R(Opts, Data->Loc, ET); |
412 | Diag(Data->Loc, DL_Error, ET, |
413 | "execution reached an unreachable program point" ); |
414 | } |
415 | |
416 | void __ubsan::__ubsan_handle_builtin_unreachable(UnreachableData *Data) { |
417 | GET_REPORT_OPTIONS(true); |
418 | handleBuiltinUnreachableImpl(Data, Opts); |
419 | Die(); |
420 | } |
421 | |
422 | static void handleMissingReturnImpl(UnreachableData *Data, ReportOptions Opts) { |
423 | ErrorType ET = ErrorType::MissingReturn; |
424 | ScopedReport R(Opts, Data->Loc, ET); |
425 | Diag(Data->Loc, DL_Error, ET, |
426 | "execution reached the end of a value-returning function " |
427 | "without returning a value" ); |
428 | } |
429 | |
430 | void __ubsan::__ubsan_handle_missing_return(UnreachableData *Data) { |
431 | GET_REPORT_OPTIONS(true); |
432 | handleMissingReturnImpl(Data, Opts); |
433 | Die(); |
434 | } |
435 | |
436 | static void handleVLABoundNotPositive(VLABoundData *Data, ValueHandle Bound, |
437 | ReportOptions Opts) { |
438 | SourceLocation Loc = Data->Loc.acquire(); |
439 | ErrorType ET = ErrorType::NonPositiveVLAIndex; |
440 | |
441 | if (ignoreReport(SLoc: Loc, Opts, ET)) |
442 | return; |
443 | |
444 | ScopedReport R(Opts, Loc, ET); |
445 | |
446 | Diag(Loc, DL_Error, ET, "variable length array bound evaluates to " |
447 | "non-positive value %0" ) |
448 | << Value(Data->Type, Bound); |
449 | } |
450 | |
451 | void __ubsan::__ubsan_handle_vla_bound_not_positive(VLABoundData *Data, |
452 | ValueHandle Bound) { |
453 | GET_REPORT_OPTIONS(false); |
454 | handleVLABoundNotPositive(Data, Bound, Opts); |
455 | } |
456 | void __ubsan::__ubsan_handle_vla_bound_not_positive_abort(VLABoundData *Data, |
457 | ValueHandle Bound) { |
458 | GET_REPORT_OPTIONS(true); |
459 | handleVLABoundNotPositive(Data, Bound, Opts); |
460 | Die(); |
461 | } |
462 | |
463 | static bool looksLikeFloatCastOverflowDataV1(void *Data) { |
464 | // First field is either a pointer to filename or a pointer to a |
465 | // TypeDescriptor. |
466 | u8 *FilenameOrTypeDescriptor; |
467 | internal_memcpy(dest: &FilenameOrTypeDescriptor, src: Data, |
468 | n: sizeof(FilenameOrTypeDescriptor)); |
469 | |
470 | // Heuristic: For float_cast_overflow, the TypeKind will be either TK_Integer |
471 | // (0x0), TK_Float (0x1) or TK_Unknown (0xff). If both types are known, |
472 | // adding both bytes will be 0 or 1 (for BE or LE). If it were a filename, |
473 | // adding two printable characters will not yield such a value. Otherwise, |
474 | // if one of them is 0xff, this is most likely TK_Unknown type descriptor. |
475 | u16 MaybeFromTypeKind = |
476 | FilenameOrTypeDescriptor[0] + FilenameOrTypeDescriptor[1]; |
477 | return MaybeFromTypeKind < 2 || FilenameOrTypeDescriptor[0] == 0xff || |
478 | FilenameOrTypeDescriptor[1] == 0xff; |
479 | } |
480 | |
481 | static void handleFloatCastOverflow(void *DataPtr, ValueHandle From, |
482 | ReportOptions Opts) { |
483 | SymbolizedStackHolder CallerLoc; |
484 | Location Loc; |
485 | const TypeDescriptor *FromType, *ToType; |
486 | ErrorType ET = ErrorType::FloatCastOverflow; |
487 | |
488 | if (looksLikeFloatCastOverflowDataV1(Data: DataPtr)) { |
489 | auto Data = reinterpret_cast<FloatCastOverflowData *>(DataPtr); |
490 | CallerLoc.reset(S: getCallerLocation(CallerPC: Opts.pc)); |
491 | Loc = CallerLoc; |
492 | FromType = &Data->FromType; |
493 | ToType = &Data->ToType; |
494 | } else { |
495 | auto Data = reinterpret_cast<FloatCastOverflowDataV2 *>(DataPtr); |
496 | SourceLocation SLoc = Data->Loc.acquire(); |
497 | if (ignoreReport(SLoc, Opts, ET)) |
498 | return; |
499 | Loc = SLoc; |
500 | FromType = &Data->FromType; |
501 | ToType = &Data->ToType; |
502 | } |
503 | |
504 | ScopedReport R(Opts, Loc, ET); |
505 | |
506 | Diag(Loc, DL_Error, ET, |
507 | "%0 is outside the range of representable values of type %2" ) |
508 | << Value(*FromType, From) << *FromType << *ToType; |
509 | } |
510 | |
511 | void __ubsan::__ubsan_handle_float_cast_overflow(void *Data, ValueHandle From) { |
512 | GET_REPORT_OPTIONS(false); |
513 | handleFloatCastOverflow(DataPtr: Data, From, Opts); |
514 | } |
515 | void __ubsan::__ubsan_handle_float_cast_overflow_abort(void *Data, |
516 | ValueHandle From) { |
517 | GET_REPORT_OPTIONS(true); |
518 | handleFloatCastOverflow(DataPtr: Data, From, Opts); |
519 | Die(); |
520 | } |
521 | |
522 | static void handleLoadInvalidValue(InvalidValueData *Data, ValueHandle Val, |
523 | ReportOptions Opts) { |
524 | SourceLocation Loc = Data->Loc.acquire(); |
525 | // This check could be more precise if we used different handlers for |
526 | // -fsanitize=bool and -fsanitize=enum. |
527 | bool IsBool = (0 == internal_strcmp(s1: Data->Type.getTypeName(), s2: "'bool'" )) || |
528 | (0 == internal_strncmp(s1: Data->Type.getTypeName(), s2: "'BOOL'" , n: 6)); |
529 | ErrorType ET = |
530 | IsBool ? ErrorType::InvalidBoolLoad : ErrorType::InvalidEnumLoad; |
531 | |
532 | if (ignoreReport(SLoc: Loc, Opts, ET)) |
533 | return; |
534 | |
535 | ScopedReport R(Opts, Loc, ET); |
536 | |
537 | Diag(Loc, DL_Error, ET, |
538 | "load of value %0, which is not a valid value for type %1" ) |
539 | << Value(Data->Type, Val) << Data->Type; |
540 | } |
541 | |
542 | void __ubsan::__ubsan_handle_load_invalid_value(InvalidValueData *Data, |
543 | ValueHandle Val) { |
544 | GET_REPORT_OPTIONS(false); |
545 | handleLoadInvalidValue(Data, Val, Opts); |
546 | } |
547 | void __ubsan::__ubsan_handle_load_invalid_value_abort(InvalidValueData *Data, |
548 | ValueHandle Val) { |
549 | GET_REPORT_OPTIONS(true); |
550 | handleLoadInvalidValue(Data, Val, Opts); |
551 | Die(); |
552 | } |
553 | |
554 | static void handleImplicitConversion(ImplicitConversionData *Data, |
555 | ReportOptions Opts, ValueHandle Src, |
556 | ValueHandle Dst) { |
557 | SourceLocation Loc = Data->Loc.acquire(); |
558 | const TypeDescriptor &SrcTy = Data->FromType; |
559 | const TypeDescriptor &DstTy = Data->ToType; |
560 | bool SrcSigned = SrcTy.isSignedIntegerTy(); |
561 | bool DstSigned = DstTy.isSignedIntegerTy(); |
562 | ErrorType ET = ErrorType::GenericUB; |
563 | |
564 | switch (Data->Kind) { |
565 | case ICCK_IntegerTruncation: { // Legacy, no longer used. |
566 | // Let's figure out what it should be as per the new types, and upgrade. |
567 | // If both types are unsigned, then it's an unsigned truncation. |
568 | // Else, it is a signed truncation. |
569 | if (!SrcSigned && !DstSigned) { |
570 | ET = ErrorType::ImplicitUnsignedIntegerTruncation; |
571 | } else { |
572 | ET = ErrorType::ImplicitSignedIntegerTruncation; |
573 | } |
574 | break; |
575 | } |
576 | case ICCK_UnsignedIntegerTruncation: |
577 | ET = ErrorType::ImplicitUnsignedIntegerTruncation; |
578 | break; |
579 | case ICCK_SignedIntegerTruncation: |
580 | ET = ErrorType::ImplicitSignedIntegerTruncation; |
581 | break; |
582 | case ICCK_IntegerSignChange: |
583 | ET = ErrorType::ImplicitIntegerSignChange; |
584 | break; |
585 | case ICCK_SignedIntegerTruncationOrSignChange: |
586 | ET = ErrorType::ImplicitSignedIntegerTruncationOrSignChange; |
587 | break; |
588 | } |
589 | |
590 | if (ignoreReport(SLoc: Loc, Opts, ET)) |
591 | return; |
592 | |
593 | ScopedReport R(Opts, Loc, ET); |
594 | |
595 | // In the case we have a bitfield, we want to explicitly say so in the |
596 | // error message. |
597 | // FIXME: is it possible to dump the values as hex with fixed width? |
598 | if (Data->BitfieldBits) |
599 | Diag(Loc, DL_Error, ET, |
600 | "implicit conversion from type %0 of value %1 (%2-bit, %3signed) to " |
601 | "type %4 changed the value to %5 (%6-bit bitfield, %7signed)" ) |
602 | << SrcTy << Value(SrcTy, Src) << SrcTy.getIntegerBitWidth() |
603 | << (SrcSigned ? "" : "un" ) << DstTy << Value(DstTy, Dst) |
604 | << Data->BitfieldBits << (DstSigned ? "" : "un" ); |
605 | else |
606 | Diag(Loc, DL_Error, ET, |
607 | "implicit conversion from type %0 of value %1 (%2-bit, %3signed) to " |
608 | "type %4 changed the value to %5 (%6-bit, %7signed)" ) |
609 | << SrcTy << Value(SrcTy, Src) << SrcTy.getIntegerBitWidth() |
610 | << (SrcSigned ? "" : "un" ) << DstTy << Value(DstTy, Dst) |
611 | << DstTy.getIntegerBitWidth() << (DstSigned ? "" : "un" ); |
612 | } |
613 | |
614 | void __ubsan::__ubsan_handle_implicit_conversion(ImplicitConversionData *Data, |
615 | ValueHandle Src, |
616 | ValueHandle Dst) { |
617 | GET_REPORT_OPTIONS(false); |
618 | handleImplicitConversion(Data, Opts, Src, Dst); |
619 | } |
620 | void __ubsan::__ubsan_handle_implicit_conversion_abort( |
621 | ImplicitConversionData *Data, ValueHandle Src, ValueHandle Dst) { |
622 | GET_REPORT_OPTIONS(true); |
623 | handleImplicitConversion(Data, Opts, Src, Dst); |
624 | Die(); |
625 | } |
626 | |
627 | static void handleInvalidBuiltin(InvalidBuiltinData *Data, ReportOptions Opts) { |
628 | SourceLocation Loc = Data->Loc.acquire(); |
629 | ErrorType ET = ErrorType::InvalidBuiltin; |
630 | |
631 | if (ignoreReport(SLoc: Loc, Opts, ET)) |
632 | return; |
633 | |
634 | ScopedReport R(Opts, Loc, ET); |
635 | |
636 | Diag(Loc, DL_Error, ET, |
637 | "passing zero to %0, which is not a valid argument" ) |
638 | << ((Data->Kind == BCK_CTZPassedZero) ? "ctz()" : "clz()" ); |
639 | } |
640 | |
641 | void __ubsan::__ubsan_handle_invalid_builtin(InvalidBuiltinData *Data) { |
642 | GET_REPORT_OPTIONS(true); |
643 | handleInvalidBuiltin(Data, Opts); |
644 | } |
645 | void __ubsan::__ubsan_handle_invalid_builtin_abort(InvalidBuiltinData *Data) { |
646 | GET_REPORT_OPTIONS(true); |
647 | handleInvalidBuiltin(Data, Opts); |
648 | Die(); |
649 | } |
650 | |
651 | static void handleInvalidObjCCast(InvalidObjCCast *Data, ValueHandle Pointer, |
652 | ReportOptions Opts) { |
653 | SourceLocation Loc = Data->Loc.acquire(); |
654 | ErrorType ET = ErrorType::InvalidObjCCast; |
655 | |
656 | if (ignoreReport(SLoc: Loc, Opts, ET)) |
657 | return; |
658 | |
659 | ScopedReport R(Opts, Loc, ET); |
660 | |
661 | const char *GivenClass = getObjCClassName(Pointer); |
662 | const char *GivenClassStr = GivenClass ? GivenClass : "<unknown type>" ; |
663 | |
664 | Diag(Loc, DL_Error, ET, |
665 | "invalid ObjC cast, object is a '%0', but expected a %1" ) |
666 | << GivenClassStr << Data->ExpectedType; |
667 | } |
668 | |
669 | void __ubsan::__ubsan_handle_invalid_objc_cast(InvalidObjCCast *Data, |
670 | ValueHandle Pointer) { |
671 | GET_REPORT_OPTIONS(false); |
672 | handleInvalidObjCCast(Data, Pointer, Opts); |
673 | } |
674 | void __ubsan::__ubsan_handle_invalid_objc_cast_abort(InvalidObjCCast *Data, |
675 | ValueHandle Pointer) { |
676 | GET_REPORT_OPTIONS(true); |
677 | handleInvalidObjCCast(Data, Pointer, Opts); |
678 | Die(); |
679 | } |
680 | |
681 | static void handleNonNullReturn(NonNullReturnData *Data, SourceLocation *LocPtr, |
682 | ReportOptions Opts, bool IsAttr) { |
683 | if (!LocPtr) |
684 | UNREACHABLE("source location pointer is null!" ); |
685 | |
686 | SourceLocation Loc = LocPtr->acquire(); |
687 | ErrorType ET = IsAttr ? ErrorType::InvalidNullReturn |
688 | : ErrorType::InvalidNullReturnWithNullability; |
689 | |
690 | if (ignoreReport(SLoc: Loc, Opts, ET)) |
691 | return; |
692 | |
693 | ScopedReport R(Opts, Loc, ET); |
694 | |
695 | Diag(Loc, DL_Error, ET, |
696 | "null pointer returned from function declared to never return null" ); |
697 | if (!Data->AttrLoc.isInvalid()) |
698 | Diag(Data->AttrLoc, DL_Note, ET, "%0 specified here" ) |
699 | << (IsAttr ? "returns_nonnull attribute" |
700 | : "_Nonnull return type annotation" ); |
701 | } |
702 | |
703 | void __ubsan::__ubsan_handle_nonnull_return_v1(NonNullReturnData *Data, |
704 | SourceLocation *LocPtr) { |
705 | GET_REPORT_OPTIONS(false); |
706 | handleNonNullReturn(Data, LocPtr, Opts, IsAttr: true); |
707 | } |
708 | |
709 | void __ubsan::__ubsan_handle_nonnull_return_v1_abort(NonNullReturnData *Data, |
710 | SourceLocation *LocPtr) { |
711 | GET_REPORT_OPTIONS(true); |
712 | handleNonNullReturn(Data, LocPtr, Opts, IsAttr: true); |
713 | Die(); |
714 | } |
715 | |
716 | void __ubsan::__ubsan_handle_nullability_return_v1(NonNullReturnData *Data, |
717 | SourceLocation *LocPtr) { |
718 | GET_REPORT_OPTIONS(false); |
719 | handleNonNullReturn(Data, LocPtr, Opts, IsAttr: false); |
720 | } |
721 | |
722 | void __ubsan::__ubsan_handle_nullability_return_v1_abort( |
723 | NonNullReturnData *Data, SourceLocation *LocPtr) { |
724 | GET_REPORT_OPTIONS(true); |
725 | handleNonNullReturn(Data, LocPtr, Opts, IsAttr: false); |
726 | Die(); |
727 | } |
728 | |
729 | static void handleNonNullArg(NonNullArgData *Data, ReportOptions Opts, |
730 | bool IsAttr) { |
731 | SourceLocation Loc = Data->Loc.acquire(); |
732 | ErrorType ET = IsAttr ? ErrorType::InvalidNullArgument |
733 | : ErrorType::InvalidNullArgumentWithNullability; |
734 | |
735 | if (ignoreReport(SLoc: Loc, Opts, ET)) |
736 | return; |
737 | |
738 | ScopedReport R(Opts, Loc, ET); |
739 | |
740 | Diag(Loc, DL_Error, ET, |
741 | "null pointer passed as argument %0, which is declared to " |
742 | "never be null" ) |
743 | << Data->ArgIndex; |
744 | if (!Data->AttrLoc.isInvalid()) |
745 | Diag(Data->AttrLoc, DL_Note, ET, "%0 specified here" ) |
746 | << (IsAttr ? "nonnull attribute" : "_Nonnull type annotation" ); |
747 | } |
748 | |
749 | void __ubsan::__ubsan_handle_nonnull_arg(NonNullArgData *Data) { |
750 | GET_REPORT_OPTIONS(false); |
751 | handleNonNullArg(Data, Opts, IsAttr: true); |
752 | } |
753 | |
754 | void __ubsan::__ubsan_handle_nonnull_arg_abort(NonNullArgData *Data) { |
755 | GET_REPORT_OPTIONS(true); |
756 | handleNonNullArg(Data, Opts, IsAttr: true); |
757 | Die(); |
758 | } |
759 | |
760 | void __ubsan::__ubsan_handle_nullability_arg(NonNullArgData *Data) { |
761 | GET_REPORT_OPTIONS(false); |
762 | handleNonNullArg(Data, Opts, IsAttr: false); |
763 | } |
764 | |
765 | void __ubsan::__ubsan_handle_nullability_arg_abort(NonNullArgData *Data) { |
766 | GET_REPORT_OPTIONS(true); |
767 | handleNonNullArg(Data, Opts, IsAttr: false); |
768 | Die(); |
769 | } |
770 | |
771 | static void handlePointerOverflowImpl(PointerOverflowData *Data, |
772 | ValueHandle Base, |
773 | ValueHandle Result, |
774 | ReportOptions Opts) { |
775 | SourceLocation Loc = Data->Loc.acquire(); |
776 | ErrorType ET; |
777 | |
778 | if (Base == 0 && Result == 0) |
779 | ET = ErrorType::NullptrWithOffset; |
780 | else if (Base == 0 && Result != 0) |
781 | ET = ErrorType::NullptrWithNonZeroOffset; |
782 | else if (Base != 0 && Result == 0) |
783 | ET = ErrorType::NullptrAfterNonZeroOffset; |
784 | else |
785 | ET = ErrorType::PointerOverflow; |
786 | |
787 | if (ignoreReport(SLoc: Loc, Opts, ET)) |
788 | return; |
789 | |
790 | ScopedReport R(Opts, Loc, ET); |
791 | |
792 | if (ET == ErrorType::NullptrWithOffset) { |
793 | Diag(Loc, DL_Error, ET, "applying zero offset to null pointer" ); |
794 | } else if (ET == ErrorType::NullptrWithNonZeroOffset) { |
795 | Diag(Loc, DL_Error, ET, "applying non-zero offset %0 to null pointer" ) |
796 | << Result; |
797 | } else if (ET == ErrorType::NullptrAfterNonZeroOffset) { |
798 | Diag( |
799 | Loc, DL_Error, ET, |
800 | "applying non-zero offset to non-null pointer %0 produced null pointer" ) |
801 | << (void *)Base; |
802 | } else if ((sptr(Base) >= 0) == (sptr(Result) >= 0)) { |
803 | if (Base > Result) |
804 | Diag(Loc, DL_Error, ET, |
805 | "addition of unsigned offset to %0 overflowed to %1" ) |
806 | << (void *)Base << (void *)Result; |
807 | else |
808 | Diag(Loc, DL_Error, ET, |
809 | "subtraction of unsigned offset from %0 overflowed to %1" ) |
810 | << (void *)Base << (void *)Result; |
811 | } else { |
812 | Diag(Loc, DL_Error, ET, |
813 | "pointer index expression with base %0 overflowed to %1" ) |
814 | << (void *)Base << (void *)Result; |
815 | } |
816 | } |
817 | |
818 | void __ubsan::__ubsan_handle_pointer_overflow(PointerOverflowData *Data, |
819 | ValueHandle Base, |
820 | ValueHandle Result) { |
821 | GET_REPORT_OPTIONS(false); |
822 | handlePointerOverflowImpl(Data, Base, Result, Opts); |
823 | } |
824 | |
825 | void __ubsan::__ubsan_handle_pointer_overflow_abort(PointerOverflowData *Data, |
826 | ValueHandle Base, |
827 | ValueHandle Result) { |
828 | GET_REPORT_OPTIONS(true); |
829 | handlePointerOverflowImpl(Data, Base, Result, Opts); |
830 | Die(); |
831 | } |
832 | |
833 | static void handleCFIBadIcall(CFICheckFailData *Data, ValueHandle Function, |
834 | ReportOptions Opts) { |
835 | if (Data->CheckKind != CFITCK_ICall && Data->CheckKind != CFITCK_NVMFCall) |
836 | Die(); |
837 | |
838 | SourceLocation Loc = Data->Loc.acquire(); |
839 | ErrorType ET = ErrorType::CFIBadType; |
840 | |
841 | if (ignoreReport(SLoc: Loc, Opts, ET)) |
842 | return; |
843 | |
844 | ScopedReport R(Opts, Loc, ET); |
845 | |
846 | const char *CheckKindStr = Data->CheckKind == CFITCK_NVMFCall |
847 | ? "non-virtual pointer to member function call" |
848 | : "indirect function call" ; |
849 | Diag(Loc, DL_Error, ET, |
850 | "control flow integrity check for type %0 failed during %1" ) |
851 | << Data->Type << CheckKindStr; |
852 | |
853 | SymbolizedStackHolder FLoc(getSymbolizedLocation(PC: Function)); |
854 | const char *FName = FLoc.get()->info.function; |
855 | if (!FName) |
856 | FName = "(unknown)" ; |
857 | Diag(FLoc, DL_Note, ET, "%0 defined here" ) << FName; |
858 | |
859 | // If the failure involved different DSOs for the check location and icall |
860 | // target, report the DSO names. |
861 | const char *DstModule = FLoc.get()->info.module; |
862 | if (!DstModule) |
863 | DstModule = "(unknown)" ; |
864 | |
865 | const char *SrcModule = Symbolizer::GetOrInit()->GetModuleNameForPc(pc: Opts.pc); |
866 | if (!SrcModule) |
867 | SrcModule = "(unknown)" ; |
868 | |
869 | if (internal_strcmp(s1: SrcModule, s2: DstModule)) |
870 | Diag(Loc, DL_Note, ET, |
871 | "check failed in %0, destination function located in %1" ) |
872 | << SrcModule << DstModule; |
873 | } |
874 | |
875 | namespace __ubsan { |
876 | |
877 | #ifdef UBSAN_CAN_USE_CXXABI |
878 | |
879 | #ifdef _WIN32 |
880 | |
881 | extern "C" void __ubsan_handle_cfi_bad_type_default(CFICheckFailData *Data, |
882 | ValueHandle Vtable, |
883 | bool ValidVtable, |
884 | ReportOptions Opts) { |
885 | Die(); |
886 | } |
887 | |
888 | WIN_WEAK_ALIAS(__ubsan_handle_cfi_bad_type, __ubsan_handle_cfi_bad_type_default) |
889 | #else |
890 | SANITIZER_WEAK_ATTRIBUTE |
891 | #endif |
892 | void __ubsan_handle_cfi_bad_type(CFICheckFailData *Data, ValueHandle Vtable, |
893 | bool ValidVtable, ReportOptions Opts); |
894 | |
895 | #else |
896 | void __ubsan_handle_cfi_bad_type(CFICheckFailData *Data, ValueHandle Vtable, |
897 | bool ValidVtable, ReportOptions Opts) { |
898 | Die(); |
899 | } |
900 | #endif |
901 | |
902 | } // namespace __ubsan |
903 | |
904 | void __ubsan::__ubsan_handle_cfi_check_fail(CFICheckFailData *Data, |
905 | ValueHandle Value, |
906 | uptr ValidVtable) { |
907 | GET_REPORT_OPTIONS(false); |
908 | if (Data->CheckKind == CFITCK_ICall || Data->CheckKind == CFITCK_NVMFCall) |
909 | handleCFIBadIcall(Data, Function: Value, Opts); |
910 | else |
911 | __ubsan_handle_cfi_bad_type(Data, Vtable: Value, ValidVtable, Opts); |
912 | } |
913 | |
914 | void __ubsan::__ubsan_handle_cfi_check_fail_abort(CFICheckFailData *Data, |
915 | ValueHandle Value, |
916 | uptr ValidVtable) { |
917 | GET_REPORT_OPTIONS(true); |
918 | if (Data->CheckKind == CFITCK_ICall || Data->CheckKind == CFITCK_NVMFCall) |
919 | handleCFIBadIcall(Data, Function: Value, Opts); |
920 | else |
921 | __ubsan_handle_cfi_bad_type(Data, Vtable: Value, ValidVtable, Opts); |
922 | Die(); |
923 | } |
924 | |
925 | static bool handleFunctionTypeMismatch(FunctionTypeMismatchData *Data, |
926 | ValueHandle Function, |
927 | ReportOptions Opts) { |
928 | SourceLocation CallLoc = Data->Loc.acquire(); |
929 | ErrorType ET = ErrorType::FunctionTypeMismatch; |
930 | if (ignoreReport(SLoc: CallLoc, Opts, ET)) |
931 | return true; |
932 | |
933 | ScopedReport R(Opts, CallLoc, ET); |
934 | |
935 | SymbolizedStackHolder FLoc(getSymbolizedLocation(PC: Function)); |
936 | const char *FName = FLoc.get()->info.function; |
937 | if (!FName) |
938 | FName = "(unknown)" ; |
939 | |
940 | Diag(CallLoc, DL_Error, ET, |
941 | "call to function %0 through pointer to incorrect function type %1" ) |
942 | << FName << Data->Type; |
943 | Diag(FLoc, DL_Note, ET, "%0 defined here" ) << FName; |
944 | return true; |
945 | } |
946 | |
947 | void __ubsan::__ubsan_handle_function_type_mismatch( |
948 | FunctionTypeMismatchData *Data, ValueHandle Function) { |
949 | GET_REPORT_OPTIONS(false); |
950 | handleFunctionTypeMismatch(Data, Function, Opts); |
951 | } |
952 | |
953 | void __ubsan::__ubsan_handle_function_type_mismatch_abort( |
954 | FunctionTypeMismatchData *Data, ValueHandle Function) { |
955 | GET_REPORT_OPTIONS(true); |
956 | if (handleFunctionTypeMismatch(Data, Function, Opts)) |
957 | Die(); |
958 | } |
959 | |
960 | #endif // CAN_SANITIZE_UB |
961 | |