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