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 handleLocalOutOfBoundsImpl(ReportOptions Opts) { |
409 | // FIXME: Pass more diagnostic info. |
410 | SymbolizedStackHolder CallerLoc; |
411 | CallerLoc.reset(S: getCallerLocation(CallerPC: Opts.pc)); |
412 | Location Loc; |
413 | Loc = CallerLoc; |
414 | ErrorType ET = ErrorType::LocalOutOfBounds; |
415 | ScopedReport R(Opts, Loc, ET); |
416 | Diag(Loc, DL_Error, ET, "access out of bounds" ); |
417 | } |
418 | |
419 | void __ubsan::__ubsan_handle_local_out_of_bounds() { |
420 | GET_REPORT_OPTIONS(false); |
421 | handleLocalOutOfBoundsImpl(Opts); |
422 | } |
423 | |
424 | void __ubsan::__ubsan_handle_local_out_of_bounds_abort() { |
425 | GET_REPORT_OPTIONS(true); |
426 | handleLocalOutOfBoundsImpl(Opts); |
427 | Die(); |
428 | } |
429 | |
430 | static void handleBuiltinUnreachableImpl(UnreachableData *Data, |
431 | ReportOptions Opts) { |
432 | ErrorType ET = ErrorType::UnreachableCall; |
433 | ScopedReport R(Opts, Data->Loc, ET); |
434 | Diag(Data->Loc, DL_Error, ET, |
435 | "execution reached an unreachable program point" ); |
436 | } |
437 | |
438 | void __ubsan::__ubsan_handle_builtin_unreachable(UnreachableData *Data) { |
439 | GET_REPORT_OPTIONS(true); |
440 | handleBuiltinUnreachableImpl(Data, Opts); |
441 | Die(); |
442 | } |
443 | |
444 | static void handleMissingReturnImpl(UnreachableData *Data, ReportOptions Opts) { |
445 | ErrorType ET = ErrorType::MissingReturn; |
446 | ScopedReport R(Opts, Data->Loc, ET); |
447 | Diag(Data->Loc, DL_Error, ET, |
448 | "execution reached the end of a value-returning function " |
449 | "without returning a value" ); |
450 | } |
451 | |
452 | void __ubsan::__ubsan_handle_missing_return(UnreachableData *Data) { |
453 | GET_REPORT_OPTIONS(true); |
454 | handleMissingReturnImpl(Data, Opts); |
455 | Die(); |
456 | } |
457 | |
458 | static void handleVLABoundNotPositive(VLABoundData *Data, ValueHandle Bound, |
459 | ReportOptions Opts) { |
460 | SourceLocation Loc = Data->Loc.acquire(); |
461 | ErrorType ET = ErrorType::NonPositiveVLAIndex; |
462 | |
463 | if (ignoreReport(SLoc: Loc, Opts, ET)) |
464 | return; |
465 | |
466 | ScopedReport R(Opts, Loc, ET); |
467 | |
468 | Diag(Loc, DL_Error, ET, "variable length array bound evaluates to " |
469 | "non-positive value %0" ) |
470 | << Value(Data->Type, Bound); |
471 | } |
472 | |
473 | void __ubsan::__ubsan_handle_vla_bound_not_positive(VLABoundData *Data, |
474 | ValueHandle Bound) { |
475 | GET_REPORT_OPTIONS(false); |
476 | handleVLABoundNotPositive(Data, Bound, Opts); |
477 | } |
478 | void __ubsan::__ubsan_handle_vla_bound_not_positive_abort(VLABoundData *Data, |
479 | ValueHandle Bound) { |
480 | GET_REPORT_OPTIONS(true); |
481 | handleVLABoundNotPositive(Data, Bound, Opts); |
482 | Die(); |
483 | } |
484 | |
485 | static bool looksLikeFloatCastOverflowDataV1(void *Data) { |
486 | // First field is either a pointer to filename or a pointer to a |
487 | // TypeDescriptor. |
488 | u8 *FilenameOrTypeDescriptor; |
489 | internal_memcpy(dest: &FilenameOrTypeDescriptor, src: Data, |
490 | n: sizeof(FilenameOrTypeDescriptor)); |
491 | |
492 | // Heuristic: For float_cast_overflow, the TypeKind will be either TK_Integer |
493 | // (0x0), TK_Float (0x1) or TK_Unknown (0xff). If both types are known, |
494 | // adding both bytes will be 0 or 1 (for BE or LE). If it were a filename, |
495 | // adding two printable characters will not yield such a value. Otherwise, |
496 | // if one of them is 0xff, this is most likely TK_Unknown type descriptor. |
497 | u16 MaybeFromTypeKind = |
498 | FilenameOrTypeDescriptor[0] + FilenameOrTypeDescriptor[1]; |
499 | return MaybeFromTypeKind < 2 || FilenameOrTypeDescriptor[0] == 0xff || |
500 | FilenameOrTypeDescriptor[1] == 0xff; |
501 | } |
502 | |
503 | static void handleFloatCastOverflow(void *DataPtr, ValueHandle From, |
504 | ReportOptions Opts) { |
505 | SymbolizedStackHolder CallerLoc; |
506 | Location Loc; |
507 | const TypeDescriptor *FromType, *ToType; |
508 | ErrorType ET = ErrorType::FloatCastOverflow; |
509 | |
510 | if (looksLikeFloatCastOverflowDataV1(Data: DataPtr)) { |
511 | auto Data = reinterpret_cast<FloatCastOverflowData *>(DataPtr); |
512 | CallerLoc.reset(S: getCallerLocation(CallerPC: Opts.pc)); |
513 | Loc = CallerLoc; |
514 | FromType = &Data->FromType; |
515 | ToType = &Data->ToType; |
516 | } else { |
517 | auto Data = reinterpret_cast<FloatCastOverflowDataV2 *>(DataPtr); |
518 | SourceLocation SLoc = Data->Loc.acquire(); |
519 | if (ignoreReport(SLoc, Opts, ET)) |
520 | return; |
521 | Loc = SLoc; |
522 | FromType = &Data->FromType; |
523 | ToType = &Data->ToType; |
524 | } |
525 | |
526 | ScopedReport R(Opts, Loc, ET); |
527 | |
528 | Diag(Loc, DL_Error, ET, |
529 | "%0 is outside the range of representable values of type %2" ) |
530 | << Value(*FromType, From) << *FromType << *ToType; |
531 | } |
532 | |
533 | void __ubsan::__ubsan_handle_float_cast_overflow(void *Data, ValueHandle From) { |
534 | GET_REPORT_OPTIONS(false); |
535 | handleFloatCastOverflow(DataPtr: Data, From, Opts); |
536 | } |
537 | void __ubsan::__ubsan_handle_float_cast_overflow_abort(void *Data, |
538 | ValueHandle From) { |
539 | GET_REPORT_OPTIONS(true); |
540 | handleFloatCastOverflow(DataPtr: Data, From, Opts); |
541 | Die(); |
542 | } |
543 | |
544 | static void handleLoadInvalidValue(InvalidValueData *Data, ValueHandle Val, |
545 | ReportOptions Opts) { |
546 | SourceLocation Loc = Data->Loc.acquire(); |
547 | // This check could be more precise if we used different handlers for |
548 | // -fsanitize=bool and -fsanitize=enum. |
549 | bool IsBool = (0 == internal_strcmp(s1: Data->Type.getTypeName(), s2: "'bool'" )) || |
550 | (0 == internal_strncmp(s1: Data->Type.getTypeName(), s2: "'BOOL'" , n: 6)); |
551 | ErrorType ET = |
552 | IsBool ? ErrorType::InvalidBoolLoad : ErrorType::InvalidEnumLoad; |
553 | |
554 | if (ignoreReport(SLoc: Loc, Opts, ET)) |
555 | return; |
556 | |
557 | ScopedReport R(Opts, Loc, ET); |
558 | |
559 | Diag(Loc, DL_Error, ET, |
560 | "load of value %0, which is not a valid value for type %1" ) |
561 | << Value(Data->Type, Val) << Data->Type; |
562 | } |
563 | |
564 | void __ubsan::__ubsan_handle_load_invalid_value(InvalidValueData *Data, |
565 | ValueHandle Val) { |
566 | GET_REPORT_OPTIONS(false); |
567 | handleLoadInvalidValue(Data, Val, Opts); |
568 | } |
569 | void __ubsan::__ubsan_handle_load_invalid_value_abort(InvalidValueData *Data, |
570 | ValueHandle Val) { |
571 | GET_REPORT_OPTIONS(true); |
572 | handleLoadInvalidValue(Data, Val, Opts); |
573 | Die(); |
574 | } |
575 | |
576 | static void handleImplicitConversion(ImplicitConversionData *Data, |
577 | ReportOptions Opts, ValueHandle Src, |
578 | ValueHandle Dst) { |
579 | SourceLocation Loc = Data->Loc.acquire(); |
580 | const TypeDescriptor &SrcTy = Data->FromType; |
581 | const TypeDescriptor &DstTy = Data->ToType; |
582 | bool SrcSigned = SrcTy.isSignedIntegerTy(); |
583 | bool DstSigned = DstTy.isSignedIntegerTy(); |
584 | ErrorType ET = ErrorType::GenericUB; |
585 | |
586 | switch (Data->Kind) { |
587 | case ICCK_IntegerTruncation: { // Legacy, no longer used. |
588 | // Let's figure out what it should be as per the new types, and upgrade. |
589 | // If both types are unsigned, then it's an unsigned truncation. |
590 | // Else, it is a signed truncation. |
591 | if (!SrcSigned && !DstSigned) { |
592 | ET = ErrorType::ImplicitUnsignedIntegerTruncation; |
593 | } else { |
594 | ET = ErrorType::ImplicitSignedIntegerTruncation; |
595 | } |
596 | break; |
597 | } |
598 | case ICCK_UnsignedIntegerTruncation: |
599 | ET = ErrorType::ImplicitUnsignedIntegerTruncation; |
600 | break; |
601 | case ICCK_SignedIntegerTruncation: |
602 | ET = ErrorType::ImplicitSignedIntegerTruncation; |
603 | break; |
604 | case ICCK_IntegerSignChange: |
605 | ET = ErrorType::ImplicitIntegerSignChange; |
606 | break; |
607 | case ICCK_SignedIntegerTruncationOrSignChange: |
608 | ET = ErrorType::ImplicitSignedIntegerTruncationOrSignChange; |
609 | break; |
610 | } |
611 | |
612 | if (ignoreReport(SLoc: Loc, Opts, ET)) |
613 | return; |
614 | |
615 | ScopedReport R(Opts, Loc, ET); |
616 | |
617 | // In the case we have a bitfield, we want to explicitly say so in the |
618 | // error message. |
619 | // FIXME: is it possible to dump the values as hex with fixed width? |
620 | if (Data->BitfieldBits) |
621 | Diag(Loc, DL_Error, ET, |
622 | "implicit conversion from type %0 of value %1 (%2-bit, %3signed) to " |
623 | "type %4 changed the value to %5 (%6-bit bitfield, %7signed)" ) |
624 | << SrcTy << Value(SrcTy, Src) << SrcTy.getIntegerBitWidth() |
625 | << (SrcSigned ? "" : "un" ) << DstTy << Value(DstTy, Dst) |
626 | << Data->BitfieldBits << (DstSigned ? "" : "un" ); |
627 | else |
628 | Diag(Loc, DL_Error, ET, |
629 | "implicit conversion from type %0 of value %1 (%2-bit, %3signed) to " |
630 | "type %4 changed the value to %5 (%6-bit, %7signed)" ) |
631 | << SrcTy << Value(SrcTy, Src) << SrcTy.getIntegerBitWidth() |
632 | << (SrcSigned ? "" : "un" ) << DstTy << Value(DstTy, Dst) |
633 | << DstTy.getIntegerBitWidth() << (DstSigned ? "" : "un" ); |
634 | } |
635 | |
636 | void __ubsan::__ubsan_handle_implicit_conversion(ImplicitConversionData *Data, |
637 | ValueHandle Src, |
638 | ValueHandle Dst) { |
639 | GET_REPORT_OPTIONS(false); |
640 | handleImplicitConversion(Data, Opts, Src, Dst); |
641 | } |
642 | void __ubsan::__ubsan_handle_implicit_conversion_abort( |
643 | ImplicitConversionData *Data, ValueHandle Src, ValueHandle Dst) { |
644 | GET_REPORT_OPTIONS(true); |
645 | handleImplicitConversion(Data, Opts, Src, Dst); |
646 | Die(); |
647 | } |
648 | |
649 | static void handleInvalidBuiltin(InvalidBuiltinData *Data, ReportOptions Opts) { |
650 | SourceLocation Loc = Data->Loc.acquire(); |
651 | ErrorType ET = ErrorType::InvalidBuiltin; |
652 | |
653 | if (ignoreReport(SLoc: Loc, Opts, ET)) |
654 | return; |
655 | |
656 | ScopedReport R(Opts, Loc, ET); |
657 | |
658 | if (Data->Kind == BCK_AssumePassedFalse) |
659 | Diag(Loc, DL_Error, ET, "assumption is violated during execution" ); |
660 | else |
661 | Diag(Loc, DL_Error, ET, |
662 | "passing zero to __builtin_%0(), which is not a valid argument" ) |
663 | << ((Data->Kind == BCK_CTZPassedZero) ? "ctz" : "clz" ); |
664 | } |
665 | |
666 | void __ubsan::__ubsan_handle_invalid_builtin(InvalidBuiltinData *Data) { |
667 | GET_REPORT_OPTIONS(false); |
668 | handleInvalidBuiltin(Data, Opts); |
669 | } |
670 | void __ubsan::__ubsan_handle_invalid_builtin_abort(InvalidBuiltinData *Data) { |
671 | GET_REPORT_OPTIONS(true); |
672 | handleInvalidBuiltin(Data, Opts); |
673 | Die(); |
674 | } |
675 | |
676 | static void handleInvalidObjCCast(InvalidObjCCast *Data, ValueHandle Pointer, |
677 | ReportOptions Opts) { |
678 | SourceLocation Loc = Data->Loc.acquire(); |
679 | ErrorType ET = ErrorType::InvalidObjCCast; |
680 | |
681 | if (ignoreReport(SLoc: Loc, Opts, ET)) |
682 | return; |
683 | |
684 | ScopedReport R(Opts, Loc, ET); |
685 | |
686 | const char *GivenClass = getObjCClassName(Pointer); |
687 | const char *GivenClassStr = GivenClass ? GivenClass : "<unknown type>" ; |
688 | |
689 | Diag(Loc, DL_Error, ET, |
690 | "invalid ObjC cast, object is a '%0', but expected a %1" ) |
691 | << GivenClassStr << Data->ExpectedType; |
692 | } |
693 | |
694 | void __ubsan::__ubsan_handle_invalid_objc_cast(InvalidObjCCast *Data, |
695 | ValueHandle Pointer) { |
696 | GET_REPORT_OPTIONS(false); |
697 | handleInvalidObjCCast(Data, Pointer, Opts); |
698 | } |
699 | void __ubsan::__ubsan_handle_invalid_objc_cast_abort(InvalidObjCCast *Data, |
700 | ValueHandle Pointer) { |
701 | GET_REPORT_OPTIONS(true); |
702 | handleInvalidObjCCast(Data, Pointer, Opts); |
703 | Die(); |
704 | } |
705 | |
706 | static void handleNonNullReturn(NonNullReturnData *Data, SourceLocation *LocPtr, |
707 | ReportOptions Opts, bool IsAttr) { |
708 | if (!LocPtr) |
709 | UNREACHABLE("source location pointer is null!" ); |
710 | |
711 | SourceLocation Loc = LocPtr->acquire(); |
712 | ErrorType ET = IsAttr ? ErrorType::InvalidNullReturn |
713 | : ErrorType::InvalidNullReturnWithNullability; |
714 | |
715 | if (ignoreReport(SLoc: Loc, Opts, ET)) |
716 | return; |
717 | |
718 | ScopedReport R(Opts, Loc, ET); |
719 | |
720 | Diag(Loc, DL_Error, ET, |
721 | "null pointer returned from function declared to never return null" ); |
722 | if (!Data->AttrLoc.isInvalid()) |
723 | Diag(Data->AttrLoc, DL_Note, ET, "%0 specified here" ) |
724 | << (IsAttr ? "returns_nonnull attribute" |
725 | : "_Nonnull return type annotation" ); |
726 | } |
727 | |
728 | void __ubsan::__ubsan_handle_nonnull_return_v1(NonNullReturnData *Data, |
729 | SourceLocation *LocPtr) { |
730 | GET_REPORT_OPTIONS(false); |
731 | handleNonNullReturn(Data, LocPtr, Opts, IsAttr: true); |
732 | } |
733 | |
734 | void __ubsan::__ubsan_handle_nonnull_return_v1_abort(NonNullReturnData *Data, |
735 | SourceLocation *LocPtr) { |
736 | GET_REPORT_OPTIONS(true); |
737 | handleNonNullReturn(Data, LocPtr, Opts, IsAttr: true); |
738 | Die(); |
739 | } |
740 | |
741 | void __ubsan::__ubsan_handle_nullability_return_v1(NonNullReturnData *Data, |
742 | SourceLocation *LocPtr) { |
743 | GET_REPORT_OPTIONS(false); |
744 | handleNonNullReturn(Data, LocPtr, Opts, IsAttr: false); |
745 | } |
746 | |
747 | void __ubsan::__ubsan_handle_nullability_return_v1_abort( |
748 | NonNullReturnData *Data, SourceLocation *LocPtr) { |
749 | GET_REPORT_OPTIONS(true); |
750 | handleNonNullReturn(Data, LocPtr, Opts, IsAttr: false); |
751 | Die(); |
752 | } |
753 | |
754 | static void handleNonNullArg(NonNullArgData *Data, ReportOptions Opts, |
755 | bool IsAttr) { |
756 | SourceLocation Loc = Data->Loc.acquire(); |
757 | ErrorType ET = IsAttr ? ErrorType::InvalidNullArgument |
758 | : ErrorType::InvalidNullArgumentWithNullability; |
759 | |
760 | if (ignoreReport(SLoc: Loc, Opts, ET)) |
761 | return; |
762 | |
763 | ScopedReport R(Opts, Loc, ET); |
764 | |
765 | Diag(Loc, DL_Error, ET, |
766 | "null pointer passed as argument %0, which is declared to " |
767 | "never be null" ) |
768 | << Data->ArgIndex; |
769 | if (!Data->AttrLoc.isInvalid()) |
770 | Diag(Data->AttrLoc, DL_Note, ET, "%0 specified here" ) |
771 | << (IsAttr ? "nonnull attribute" : "_Nonnull type annotation" ); |
772 | } |
773 | |
774 | void __ubsan::__ubsan_handle_nonnull_arg(NonNullArgData *Data) { |
775 | GET_REPORT_OPTIONS(false); |
776 | handleNonNullArg(Data, Opts, IsAttr: true); |
777 | } |
778 | |
779 | void __ubsan::__ubsan_handle_nonnull_arg_abort(NonNullArgData *Data) { |
780 | GET_REPORT_OPTIONS(true); |
781 | handleNonNullArg(Data, Opts, IsAttr: true); |
782 | Die(); |
783 | } |
784 | |
785 | void __ubsan::__ubsan_handle_nullability_arg(NonNullArgData *Data) { |
786 | GET_REPORT_OPTIONS(false); |
787 | handleNonNullArg(Data, Opts, IsAttr: false); |
788 | } |
789 | |
790 | void __ubsan::__ubsan_handle_nullability_arg_abort(NonNullArgData *Data) { |
791 | GET_REPORT_OPTIONS(true); |
792 | handleNonNullArg(Data, Opts, IsAttr: false); |
793 | Die(); |
794 | } |
795 | |
796 | static void handlePointerOverflowImpl(PointerOverflowData *Data, |
797 | ValueHandle Base, |
798 | ValueHandle Result, |
799 | ReportOptions Opts) { |
800 | SourceLocation Loc = Data->Loc.acquire(); |
801 | ErrorType ET; |
802 | |
803 | if (Base == 0 && Result == 0) |
804 | ET = ErrorType::NullptrWithOffset; |
805 | else if (Base == 0 && Result != 0) |
806 | ET = ErrorType::NullptrWithNonZeroOffset; |
807 | else if (Base != 0 && Result == 0) |
808 | ET = ErrorType::NullptrAfterNonZeroOffset; |
809 | else |
810 | ET = ErrorType::PointerOverflow; |
811 | |
812 | if (ignoreReport(SLoc: Loc, Opts, ET)) |
813 | return; |
814 | |
815 | ScopedReport R(Opts, Loc, ET); |
816 | |
817 | if (ET == ErrorType::NullptrWithOffset) { |
818 | Diag(Loc, DL_Error, ET, "applying zero offset to null pointer" ); |
819 | } else if (ET == ErrorType::NullptrWithNonZeroOffset) { |
820 | Diag(Loc, DL_Error, ET, "applying non-zero offset %0 to null pointer" ) |
821 | << Result; |
822 | } else if (ET == ErrorType::NullptrAfterNonZeroOffset) { |
823 | Diag( |
824 | Loc, DL_Error, ET, |
825 | "applying non-zero offset to non-null pointer %0 produced null pointer" ) |
826 | << (void *)Base; |
827 | } else if ((sptr(Base) >= 0) == (sptr(Result) >= 0)) { |
828 | if (Base > Result) |
829 | Diag(Loc, DL_Error, ET, |
830 | "addition of unsigned offset to %0 overflowed to %1" ) |
831 | << (void *)Base << (void *)Result; |
832 | else |
833 | Diag(Loc, DL_Error, ET, |
834 | "subtraction of unsigned offset from %0 overflowed to %1" ) |
835 | << (void *)Base << (void *)Result; |
836 | } else { |
837 | Diag(Loc, DL_Error, ET, |
838 | "pointer index expression with base %0 overflowed to %1" ) |
839 | << (void *)Base << (void *)Result; |
840 | } |
841 | } |
842 | |
843 | void __ubsan::__ubsan_handle_pointer_overflow(PointerOverflowData *Data, |
844 | ValueHandle Base, |
845 | ValueHandle Result) { |
846 | GET_REPORT_OPTIONS(false); |
847 | handlePointerOverflowImpl(Data, Base, Result, Opts); |
848 | } |
849 | |
850 | void __ubsan::__ubsan_handle_pointer_overflow_abort(PointerOverflowData *Data, |
851 | ValueHandle Base, |
852 | ValueHandle Result) { |
853 | GET_REPORT_OPTIONS(true); |
854 | handlePointerOverflowImpl(Data, Base, Result, Opts); |
855 | Die(); |
856 | } |
857 | |
858 | static void handleCFIBadIcall(CFICheckFailData *Data, ValueHandle Function, |
859 | ReportOptions Opts) { |
860 | if (Data->CheckKind != CFITCK_ICall && Data->CheckKind != CFITCK_NVMFCall) |
861 | Die(); |
862 | |
863 | SourceLocation Loc = Data->Loc.acquire(); |
864 | ErrorType ET = ErrorType::CFIBadType; |
865 | |
866 | if (ignoreReport(SLoc: Loc, Opts, ET)) |
867 | return; |
868 | |
869 | ScopedReport R(Opts, Loc, ET); |
870 | |
871 | const char *CheckKindStr = Data->CheckKind == CFITCK_NVMFCall |
872 | ? "non-virtual pointer to member function call" |
873 | : "indirect function call" ; |
874 | Diag(Loc, DL_Error, ET, |
875 | "control flow integrity check for type %0 failed during %1" ) |
876 | << Data->Type << CheckKindStr; |
877 | |
878 | SymbolizedStackHolder FLoc(getSymbolizedLocation(PC: Function)); |
879 | const char *FName = FLoc.get()->info.function; |
880 | if (!FName) |
881 | FName = "(unknown)" ; |
882 | Diag(FLoc, DL_Note, ET, "%0 defined here" ) << FName; |
883 | |
884 | // If the failure involved different DSOs for the check location and icall |
885 | // target, report the DSO names. |
886 | const char *DstModule = FLoc.get()->info.module; |
887 | if (!DstModule) |
888 | DstModule = "(unknown)" ; |
889 | |
890 | const char *SrcModule = Symbolizer::GetOrInit()->GetModuleNameForPc(pc: Opts.pc); |
891 | if (!SrcModule) |
892 | SrcModule = "(unknown)" ; |
893 | |
894 | if (internal_strcmp(s1: SrcModule, s2: DstModule)) |
895 | Diag(Loc, DL_Note, ET, |
896 | "check failed in %0, destination function located in %1" ) |
897 | << SrcModule << DstModule; |
898 | } |
899 | |
900 | namespace __ubsan { |
901 | |
902 | #ifdef _WIN32 |
903 | extern "C" void __ubsan_handle_cfi_bad_type_default(CFICheckFailData *Data, |
904 | ValueHandle Vtable, |
905 | bool ValidVtable, |
906 | ReportOptions Opts) { |
907 | Die(); |
908 | } |
909 | |
910 | WIN_WEAK_ALIAS(__ubsan_handle_cfi_bad_type, __ubsan_handle_cfi_bad_type_default) |
911 | void __ubsan_handle_cfi_bad_type(CFICheckFailData *Data, ValueHandle Vtable, |
912 | bool ValidVtable, ReportOptions Opts); |
913 | #else |
914 | SANITIZER_WEAK_ATTRIBUTE |
915 | void __ubsan_handle_cfi_bad_type(CFICheckFailData *Data, ValueHandle Vtable, |
916 | bool ValidVtable, ReportOptions Opts) { |
917 | Die(); |
918 | } |
919 | #endif |
920 | |
921 | } // namespace __ubsan |
922 | |
923 | void __ubsan::__ubsan_handle_cfi_check_fail(CFICheckFailData *Data, |
924 | ValueHandle Value, |
925 | uptr ValidVtable) { |
926 | GET_REPORT_OPTIONS(false); |
927 | if (Data->CheckKind == CFITCK_ICall || Data->CheckKind == CFITCK_NVMFCall) |
928 | handleCFIBadIcall(Data, Function: Value, Opts); |
929 | else |
930 | __ubsan_handle_cfi_bad_type(Data, Vtable: Value, ValidVtable, Opts); |
931 | } |
932 | |
933 | void __ubsan::__ubsan_handle_cfi_check_fail_abort(CFICheckFailData *Data, |
934 | ValueHandle Value, |
935 | uptr ValidVtable) { |
936 | GET_REPORT_OPTIONS(true); |
937 | if (Data->CheckKind == CFITCK_ICall || Data->CheckKind == CFITCK_NVMFCall) |
938 | handleCFIBadIcall(Data, Function: Value, Opts); |
939 | else |
940 | __ubsan_handle_cfi_bad_type(Data, Vtable: Value, ValidVtable, Opts); |
941 | Die(); |
942 | } |
943 | |
944 | static bool handleFunctionTypeMismatch(FunctionTypeMismatchData *Data, |
945 | ValueHandle Function, |
946 | ReportOptions Opts) { |
947 | SourceLocation CallLoc = Data->Loc.acquire(); |
948 | ErrorType ET = ErrorType::FunctionTypeMismatch; |
949 | if (ignoreReport(SLoc: CallLoc, Opts, ET)) |
950 | return true; |
951 | |
952 | ScopedReport R(Opts, CallLoc, ET); |
953 | |
954 | SymbolizedStackHolder FLoc(getSymbolizedLocation(PC: Function)); |
955 | const char *FName = FLoc.get()->info.function; |
956 | if (!FName) |
957 | FName = "(unknown)" ; |
958 | |
959 | Diag(CallLoc, DL_Error, ET, |
960 | "call to function %0 through pointer to incorrect function type %1" ) |
961 | << FName << Data->Type; |
962 | Diag(FLoc, DL_Note, ET, "%0 defined here" ) << FName; |
963 | return true; |
964 | } |
965 | |
966 | void __ubsan::__ubsan_handle_function_type_mismatch( |
967 | FunctionTypeMismatchData *Data, ValueHandle Function) { |
968 | GET_REPORT_OPTIONS(false); |
969 | handleFunctionTypeMismatch(Data, Function, Opts); |
970 | } |
971 | |
972 | void __ubsan::__ubsan_handle_function_type_mismatch_abort( |
973 | FunctionTypeMismatchData *Data, ValueHandle Function) { |
974 | GET_REPORT_OPTIONS(true); |
975 | if (handleFunctionTypeMismatch(Data, Function, Opts)) |
976 | Die(); |
977 | } |
978 | |
979 | #endif // CAN_SANITIZE_UB |
980 | |