1 //===-- ubsan_handlers.cc -------------------------------------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // Error logging entry points for the UBSan runtime.
12 //===----------------------------------------------------------------------===//
14 #include "ubsan_platform.h"
16 #include "ubsan_handlers.h"
17 #include "ubsan_diag.h"
19 #include "sanitizer_common/sanitizer_common.h"
21 using namespace __sanitizer
;
22 using namespace __ubsan
;
24 static bool ignoreReport(SourceLocation SLoc
, ReportOptions Opts
) {
25 // If source location is already acquired, we don't need to print an error
26 // report for the second time. However, if we're in an unrecoverable handler,
27 // it's possible that location was required by concurrently running thread.
28 // In this case, we should continue the execution to ensure that any of
29 // threads will grab the report mutex and print the report before
30 // crashing the program.
31 return SLoc
.isDisabled() && !Opts
.DieAfterReport
;
35 const char *TypeCheckKinds
[] = {
36 "load of", "store to", "reference binding to", "member access within",
37 "member call on", "constructor call on", "downcast of", "downcast of",
38 "upcast of", "cast to virtual base of"};
41 static void handleTypeMismatchImpl(TypeMismatchData
*Data
, ValueHandle Pointer
,
43 Location Loc
= Data
->Loc
.acquire();
44 // Use the SourceLocation from Data to track deduplication, even if 'invalid'
45 if (ignoreReport(Loc
.getSourceLocation(), Opts
))
48 SymbolizedStackHolder FallbackLoc
;
49 if (Data
->Loc
.isInvalid()) {
50 FallbackLoc
.reset(getCallerLocation(Opts
.pc
));
54 ScopedReport
R(Opts
, Loc
);
57 R
.setErrorType(ErrorType::NullPointerUse
);
58 Diag(Loc
, DL_Error
, "%0 null pointer of type %1")
59 << TypeCheckKinds
[Data
->TypeCheckKind
] << Data
->Type
;
60 } else if (Data
->Alignment
&& (Pointer
& (Data
->Alignment
- 1))) {
61 R
.setErrorType(ErrorType::MisalignedPointerUse
);
62 Diag(Loc
, DL_Error
, "%0 misaligned address %1 for type %3, "
63 "which requires %2 byte alignment")
64 << TypeCheckKinds
[Data
->TypeCheckKind
] << (void*)Pointer
65 << Data
->Alignment
<< Data
->Type
;
67 R
.setErrorType(ErrorType::InsufficientObjectSize
);
68 Diag(Loc
, DL_Error
, "%0 address %1 with insufficient space "
69 "for an object of type %2")
70 << TypeCheckKinds
[Data
->TypeCheckKind
] << (void*)Pointer
<< Data
->Type
;
73 Diag(Pointer
, DL_Note
, "pointer points here");
76 void __ubsan::__ubsan_handle_type_mismatch(TypeMismatchData
*Data
,
77 ValueHandle Pointer
) {
78 GET_REPORT_OPTIONS(false);
79 handleTypeMismatchImpl(Data
, Pointer
, Opts
);
81 void __ubsan::__ubsan_handle_type_mismatch_abort(TypeMismatchData
*Data
,
82 ValueHandle Pointer
) {
83 GET_REPORT_OPTIONS(true);
84 handleTypeMismatchImpl(Data
, Pointer
, Opts
);
88 /// \brief Common diagnostic emission for various forms of integer overflow.
90 static void handleIntegerOverflowImpl(OverflowData
*Data
, ValueHandle LHS
,
91 const char *Operator
, T RHS
,
93 SourceLocation Loc
= Data
->Loc
.acquire();
94 if (ignoreReport(Loc
, Opts
))
97 bool IsSigned
= Data
->Type
.isSignedIntegerTy();
98 ScopedReport
R(Opts
, Loc
, IsSigned
? ErrorType::SignedIntegerOverflow
99 : ErrorType::UnsignedIntegerOverflow
);
101 Diag(Loc
, DL_Error
, "%0 integer overflow: "
102 "%1 %2 %3 cannot be represented in type %4")
103 << (IsSigned
? "signed" : "unsigned")
104 << Value(Data
->Type
, LHS
) << Operator
<< RHS
<< Data
->Type
;
107 #define UBSAN_OVERFLOW_HANDLER(handler_name, op, abort) \
108 void __ubsan::handler_name(OverflowData *Data, ValueHandle LHS, \
110 GET_REPORT_OPTIONS(abort); \
111 handleIntegerOverflowImpl(Data, LHS, op, Value(Data->Type, RHS), Opts); \
115 UBSAN_OVERFLOW_HANDLER(__ubsan_handle_add_overflow
, "+", false)
116 UBSAN_OVERFLOW_HANDLER(__ubsan_handle_add_overflow_abort
, "+", true)
117 UBSAN_OVERFLOW_HANDLER(__ubsan_handle_sub_overflow
, "-", false)
118 UBSAN_OVERFLOW_HANDLER(__ubsan_handle_sub_overflow_abort
, "-", true)
119 UBSAN_OVERFLOW_HANDLER(__ubsan_handle_mul_overflow
, "*", false)
120 UBSAN_OVERFLOW_HANDLER(__ubsan_handle_mul_overflow_abort
, "*", true)
122 static void handleNegateOverflowImpl(OverflowData
*Data
, ValueHandle OldVal
,
123 ReportOptions Opts
) {
124 SourceLocation Loc
= Data
->Loc
.acquire();
125 if (ignoreReport(Loc
, Opts
))
128 bool IsSigned
= Data
->Type
.isSignedIntegerTy();
129 ScopedReport
R(Opts
, Loc
, IsSigned
? ErrorType::SignedIntegerOverflow
130 : ErrorType::UnsignedIntegerOverflow
);
134 "negation of %0 cannot be represented in type %1; "
135 "cast to an unsigned type to negate this value to itself")
136 << Value(Data
->Type
, OldVal
) << Data
->Type
;
138 Diag(Loc
, DL_Error
, "negation of %0 cannot be represented in type %1")
139 << Value(Data
->Type
, OldVal
) << Data
->Type
;
142 void __ubsan::__ubsan_handle_negate_overflow(OverflowData
*Data
,
143 ValueHandle OldVal
) {
144 GET_REPORT_OPTIONS(false);
145 handleNegateOverflowImpl(Data
, OldVal
, Opts
);
147 void __ubsan::__ubsan_handle_negate_overflow_abort(OverflowData
*Data
,
148 ValueHandle OldVal
) {
149 GET_REPORT_OPTIONS(true);
150 handleNegateOverflowImpl(Data
, OldVal
, Opts
);
154 static void handleDivremOverflowImpl(OverflowData
*Data
, ValueHandle LHS
,
155 ValueHandle RHS
, ReportOptions Opts
) {
156 SourceLocation Loc
= Data
->Loc
.acquire();
157 if (ignoreReport(Loc
, Opts
))
160 ScopedReport
R(Opts
, Loc
);
162 Value
LHSVal(Data
->Type
, LHS
);
163 Value
RHSVal(Data
->Type
, RHS
);
164 if (RHSVal
.isMinusOne()) {
165 R
.setErrorType(ErrorType::SignedIntegerOverflow
);
167 "division of %0 by -1 cannot be represented in type %1")
168 << LHSVal
<< Data
->Type
;
170 R
.setErrorType(Data
->Type
.isIntegerTy() ? ErrorType::IntegerDivideByZero
171 : ErrorType::FloatDivideByZero
);
172 Diag(Loc
, DL_Error
, "division by zero");
176 void __ubsan::__ubsan_handle_divrem_overflow(OverflowData
*Data
,
177 ValueHandle LHS
, ValueHandle RHS
) {
178 GET_REPORT_OPTIONS(false);
179 handleDivremOverflowImpl(Data
, LHS
, RHS
, Opts
);
181 void __ubsan::__ubsan_handle_divrem_overflow_abort(OverflowData
*Data
,
184 GET_REPORT_OPTIONS(true);
185 handleDivremOverflowImpl(Data
, LHS
, RHS
, Opts
);
189 static void handleShiftOutOfBoundsImpl(ShiftOutOfBoundsData
*Data
,
190 ValueHandle LHS
, ValueHandle RHS
,
191 ReportOptions Opts
) {
192 SourceLocation Loc
= Data
->Loc
.acquire();
193 if (ignoreReport(Loc
, Opts
))
196 ScopedReport
R(Opts
, Loc
);
198 Value
LHSVal(Data
->LHSType
, LHS
);
199 Value
RHSVal(Data
->RHSType
, RHS
);
200 if (RHSVal
.isNegative()) {
201 R
.setErrorType(ErrorType::InvalidShiftExponent
);
202 Diag(Loc
, DL_Error
, "shift exponent %0 is negative") << RHSVal
;
203 } else if (RHSVal
.getPositiveIntValue() >=
204 Data
->LHSType
.getIntegerBitWidth()) {
205 R
.setErrorType(ErrorType::InvalidShiftExponent
);
206 Diag(Loc
, DL_Error
, "shift exponent %0 is too large for %1-bit type %2")
207 << RHSVal
<< Data
->LHSType
.getIntegerBitWidth() << Data
->LHSType
;
208 } else if (LHSVal
.isNegative()) {
209 R
.setErrorType(ErrorType::InvalidShiftBase
);
210 Diag(Loc
, DL_Error
, "left shift of negative value %0") << LHSVal
;
212 R
.setErrorType(ErrorType::InvalidShiftBase
);
214 "left shift of %0 by %1 places cannot be represented in type %2")
215 << LHSVal
<< RHSVal
<< Data
->LHSType
;
219 void __ubsan::__ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData
*Data
,
222 GET_REPORT_OPTIONS(false);
223 handleShiftOutOfBoundsImpl(Data
, LHS
, RHS
, Opts
);
225 void __ubsan::__ubsan_handle_shift_out_of_bounds_abort(
226 ShiftOutOfBoundsData
*Data
,
229 GET_REPORT_OPTIONS(true);
230 handleShiftOutOfBoundsImpl(Data
, LHS
, RHS
, Opts
);
234 static void handleOutOfBoundsImpl(OutOfBoundsData
*Data
, ValueHandle Index
,
235 ReportOptions Opts
) {
236 SourceLocation Loc
= Data
->Loc
.acquire();
237 if (ignoreReport(Loc
, Opts
))
240 ScopedReport
R(Opts
, Loc
, ErrorType::OutOfBoundsIndex
);
242 Value
IndexVal(Data
->IndexType
, Index
);
243 Diag(Loc
, DL_Error
, "index %0 out of bounds for type %1")
244 << IndexVal
<< Data
->ArrayType
;
247 void __ubsan::__ubsan_handle_out_of_bounds(OutOfBoundsData
*Data
,
249 GET_REPORT_OPTIONS(false);
250 handleOutOfBoundsImpl(Data
, Index
, Opts
);
252 void __ubsan::__ubsan_handle_out_of_bounds_abort(OutOfBoundsData
*Data
,
254 GET_REPORT_OPTIONS(true);
255 handleOutOfBoundsImpl(Data
, Index
, Opts
);
259 static void handleBuiltinUnreachableImpl(UnreachableData
*Data
,
260 ReportOptions Opts
) {
261 ScopedReport
R(Opts
, Data
->Loc
, ErrorType::UnreachableCall
);
262 Diag(Data
->Loc
, DL_Error
, "execution reached a __builtin_unreachable() call");
265 void __ubsan::__ubsan_handle_builtin_unreachable(UnreachableData
*Data
) {
266 GET_REPORT_OPTIONS(true);
267 handleBuiltinUnreachableImpl(Data
, Opts
);
271 static void handleMissingReturnImpl(UnreachableData
*Data
, ReportOptions Opts
) {
272 ScopedReport
R(Opts
, Data
->Loc
, ErrorType::MissingReturn
);
273 Diag(Data
->Loc
, DL_Error
,
274 "execution reached the end of a value-returning function "
275 "without returning a value");
278 void __ubsan::__ubsan_handle_missing_return(UnreachableData
*Data
) {
279 GET_REPORT_OPTIONS(true);
280 handleMissingReturnImpl(Data
, Opts
);
284 static void handleVLABoundNotPositive(VLABoundData
*Data
, ValueHandle Bound
,
285 ReportOptions Opts
) {
286 SourceLocation Loc
= Data
->Loc
.acquire();
287 if (ignoreReport(Loc
, Opts
))
290 ScopedReport
R(Opts
, Loc
, ErrorType::NonPositiveVLAIndex
);
292 Diag(Loc
, DL_Error
, "variable length array bound evaluates to "
293 "non-positive value %0")
294 << Value(Data
->Type
, Bound
);
297 void __ubsan::__ubsan_handle_vla_bound_not_positive(VLABoundData
*Data
,
299 GET_REPORT_OPTIONS(false);
300 handleVLABoundNotPositive(Data
, Bound
, Opts
);
302 void __ubsan::__ubsan_handle_vla_bound_not_positive_abort(VLABoundData
*Data
,
304 GET_REPORT_OPTIONS(true);
305 handleVLABoundNotPositive(Data
, Bound
, Opts
);
309 static bool looksLikeFloatCastOverflowDataV1(void *Data
) {
310 // First field is either a pointer to filename or a pointer to a
312 u8
*FilenameOrTypeDescriptor
;
313 internal_memcpy(&FilenameOrTypeDescriptor
, Data
,
314 sizeof(FilenameOrTypeDescriptor
));
316 // Heuristic: For float_cast_overflow, the TypeKind will be either TK_Integer
317 // (0x0), TK_Float (0x1) or TK_Unknown (0xff). If both types are known,
318 // adding both bytes will be 0 or 1 (for BE or LE). If it were a filename,
319 // adding two printable characters will not yield such a value. Otherwise,
320 // if one of them is 0xff, this is most likely TK_Unknown type descriptor.
321 u16 MaybeFromTypeKind
=
322 FilenameOrTypeDescriptor
[0] + FilenameOrTypeDescriptor
[1];
323 return MaybeFromTypeKind
< 2 || FilenameOrTypeDescriptor
[0] == 0xff ||
324 FilenameOrTypeDescriptor
[1] == 0xff;
327 static void handleFloatCastOverflow(void *DataPtr
, ValueHandle From
,
328 ReportOptions Opts
) {
329 SymbolizedStackHolder CallerLoc
;
331 const TypeDescriptor
*FromType
, *ToType
;
333 if (looksLikeFloatCastOverflowDataV1(DataPtr
)) {
334 auto Data
= reinterpret_cast<FloatCastOverflowData
*>(DataPtr
);
335 CallerLoc
.reset(getCallerLocation(Opts
.pc
));
337 FromType
= &Data
->FromType
;
338 ToType
= &Data
->ToType
;
340 auto Data
= reinterpret_cast<FloatCastOverflowDataV2
*>(DataPtr
);
341 SourceLocation SLoc
= Data
->Loc
.acquire();
342 if (ignoreReport(SLoc
, Opts
))
345 FromType
= &Data
->FromType
;
346 ToType
= &Data
->ToType
;
349 ScopedReport
R(Opts
, Loc
, ErrorType::FloatCastOverflow
);
352 "value %0 is outside the range of representable values of type %2")
353 << Value(*FromType
, From
) << *FromType
<< *ToType
;
356 void __ubsan::__ubsan_handle_float_cast_overflow(void *Data
, ValueHandle From
) {
357 GET_REPORT_OPTIONS(false);
358 handleFloatCastOverflow(Data
, From
, Opts
);
360 void __ubsan::__ubsan_handle_float_cast_overflow_abort(void *Data
,
362 GET_REPORT_OPTIONS(true);
363 handleFloatCastOverflow(Data
, From
, Opts
);
367 static void handleLoadInvalidValue(InvalidValueData
*Data
, ValueHandle Val
,
368 ReportOptions Opts
) {
369 SourceLocation Loc
= Data
->Loc
.acquire();
370 if (ignoreReport(Loc
, Opts
))
373 // This check could be more precise if we used different handlers for
374 // -fsanitize=bool and -fsanitize=enum.
375 bool IsBool
= (0 == internal_strcmp(Data
->Type
.getTypeName(), "'bool'"));
376 ScopedReport
R(Opts
, Loc
, IsBool
? ErrorType::InvalidBoolLoad
377 : ErrorType::InvalidEnumLoad
);
380 "load of value %0, which is not a valid value for type %1")
381 << Value(Data
->Type
, Val
) << Data
->Type
;
384 void __ubsan::__ubsan_handle_load_invalid_value(InvalidValueData
*Data
,
386 GET_REPORT_OPTIONS(false);
387 handleLoadInvalidValue(Data
, Val
, Opts
);
389 void __ubsan::__ubsan_handle_load_invalid_value_abort(InvalidValueData
*Data
,
391 GET_REPORT_OPTIONS(true);
392 handleLoadInvalidValue(Data
, Val
, Opts
);
396 static void handleFunctionTypeMismatch(FunctionTypeMismatchData
*Data
,
397 ValueHandle Function
,
398 ReportOptions Opts
) {
399 SourceLocation CallLoc
= Data
->Loc
.acquire();
400 if (ignoreReport(CallLoc
, Opts
))
403 ScopedReport
R(Opts
, CallLoc
, ErrorType::FunctionTypeMismatch
);
405 SymbolizedStackHolder
FLoc(getSymbolizedLocation(Function
));
406 const char *FName
= FLoc
.get()->info
.function
;
410 Diag(CallLoc
, DL_Error
,
411 "call to function %0 through pointer to incorrect function type %1")
412 << FName
<< Data
->Type
;
413 Diag(FLoc
, DL_Note
, "%0 defined here") << FName
;
417 __ubsan::__ubsan_handle_function_type_mismatch(FunctionTypeMismatchData
*Data
,
418 ValueHandle Function
) {
419 GET_REPORT_OPTIONS(false);
420 handleFunctionTypeMismatch(Data
, Function
, Opts
);
423 void __ubsan::__ubsan_handle_function_type_mismatch_abort(
424 FunctionTypeMismatchData
*Data
, ValueHandle Function
) {
425 GET_REPORT_OPTIONS(true);
426 handleFunctionTypeMismatch(Data
, Function
, Opts
);
430 static void handleNonNullReturn(NonNullReturnData
*Data
, ReportOptions Opts
) {
431 SourceLocation Loc
= Data
->Loc
.acquire();
432 if (ignoreReport(Loc
, Opts
))
435 ScopedReport
R(Opts
, Loc
, ErrorType::InvalidNullReturn
);
437 Diag(Loc
, DL_Error
, "null pointer returned from function declared to never "
439 if (!Data
->AttrLoc
.isInvalid())
440 Diag(Data
->AttrLoc
, DL_Note
, "returns_nonnull attribute specified here");
443 void __ubsan::__ubsan_handle_nonnull_return(NonNullReturnData
*Data
) {
444 GET_REPORT_OPTIONS(false);
445 handleNonNullReturn(Data
, Opts
);
448 void __ubsan::__ubsan_handle_nonnull_return_abort(NonNullReturnData
*Data
) {
449 GET_REPORT_OPTIONS(true);
450 handleNonNullReturn(Data
, Opts
);
454 static void handleNonNullArg(NonNullArgData
*Data
, ReportOptions Opts
) {
455 SourceLocation Loc
= Data
->Loc
.acquire();
456 if (ignoreReport(Loc
, Opts
))
459 ScopedReport
R(Opts
, Loc
, ErrorType::InvalidNullArgument
);
461 Diag(Loc
, DL_Error
, "null pointer passed as argument %0, which is declared to "
462 "never be null") << Data
->ArgIndex
;
463 if (!Data
->AttrLoc
.isInvalid())
464 Diag(Data
->AttrLoc
, DL_Note
, "nonnull attribute specified here");
467 void __ubsan::__ubsan_handle_nonnull_arg(NonNullArgData
*Data
) {
468 GET_REPORT_OPTIONS(false);
469 handleNonNullArg(Data
, Opts
);
472 void __ubsan::__ubsan_handle_nonnull_arg_abort(NonNullArgData
*Data
) {
473 GET_REPORT_OPTIONS(true);
474 handleNonNullArg(Data
, Opts
);
478 static void handleCFIBadIcall(CFIBadIcallData
*Data
, ValueHandle Function
,
479 ReportOptions Opts
) {
480 SourceLocation Loc
= Data
->Loc
.acquire();
481 if (ignoreReport(Loc
, Opts
))
484 ScopedReport
R(Opts
, Loc
);
486 Diag(Loc
, DL_Error
, "control flow integrity check for type %0 failed during "
487 "indirect function call")
490 SymbolizedStackHolder
FLoc(getSymbolizedLocation(Function
));
491 const char *FName
= FLoc
.get()->info
.function
;
494 Diag(FLoc
, DL_Note
, "%0 defined here") << FName
;
497 void __ubsan::__ubsan_handle_cfi_bad_icall(CFIBadIcallData
*Data
,
498 ValueHandle Function
) {
499 GET_REPORT_OPTIONS(false);
500 handleCFIBadIcall(Data
, Function
, Opts
);
503 void __ubsan::__ubsan_handle_cfi_bad_icall_abort(CFIBadIcallData
*Data
,
504 ValueHandle Function
) {
505 GET_REPORT_OPTIONS(true);
506 handleCFIBadIcall(Data
, Function
, Opts
);
510 #endif // CAN_SANITIZE_UB