1 //===-- ubsan_handlers.cc -------------------------------------------------===//
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
6 //===----------------------------------------------------------------------===//
8 // Error logging entry points for the UBSan runtime.
10 //===----------------------------------------------------------------------===//
12 #include "ubsan_platform.h"
14 #include "ubsan_handlers.h"
15 #include "ubsan_diag.h"
17 #include "sanitizer_common/sanitizer_common.h"
19 using namespace __sanitizer
;
20 using namespace __ubsan
;
22 static bool ignoreReport(SourceLocation SLoc
, ReportOptions Opts
) {
23 // If source location is already acquired, we don't need to print an error
24 // report for the second time. However, if we're in an unrecoverable handler,
25 // it's possible that location was required by concurrently running thread.
26 // In this case, we should continue the execution to ensure that any of
27 // threads will grab the report mutex and print the report before
28 // crashing the program.
29 return SLoc
.isDisabled() && !Opts
.DieAfterReport
;
33 const char *TypeCheckKinds
[] = {
34 "load of", "store to", "reference binding to", "member access within",
35 "member call on", "constructor call on", "downcast of", "downcast of",
36 "upcast of", "cast to virtual base of"};
39 static void handleTypeMismatchImpl(TypeMismatchData
*Data
, ValueHandle Pointer
,
41 Location Loc
= Data
->Loc
.acquire();
42 // Use the SourceLocation from Data to track deduplication, even if 'invalid'
43 if (ignoreReport(Loc
.getSourceLocation(), Opts
))
46 SymbolizedStackHolder FallbackLoc
;
47 if (Data
->Loc
.isInvalid()) {
48 FallbackLoc
.reset(getCallerLocation(Opts
.pc
));
52 ScopedReport
R(Opts
, Loc
);
55 R
.setErrorType(ErrorType::NullPointerUse
);
56 Diag(Loc
, DL_Error
, "%0 null pointer of type %1")
57 << TypeCheckKinds
[Data
->TypeCheckKind
] << Data
->Type
;
58 } else if (Data
->Alignment
&& (Pointer
& (Data
->Alignment
- 1))) {
59 R
.setErrorType(ErrorType::MisalignedPointerUse
);
60 Diag(Loc
, DL_Error
, "%0 misaligned address %1 for type %3, "
61 "which requires %2 byte alignment")
62 << TypeCheckKinds
[Data
->TypeCheckKind
] << (void*)Pointer
63 << Data
->Alignment
<< Data
->Type
;
65 R
.setErrorType(ErrorType::InsufficientObjectSize
);
66 Diag(Loc
, DL_Error
, "%0 address %1 with insufficient space "
67 "for an object of type %2")
68 << TypeCheckKinds
[Data
->TypeCheckKind
] << (void*)Pointer
<< Data
->Type
;
71 Diag(Pointer
, DL_Note
, "pointer points here");
74 void __ubsan::__ubsan_handle_type_mismatch(TypeMismatchData
*Data
,
75 ValueHandle Pointer
) {
76 GET_REPORT_OPTIONS(false);
77 handleTypeMismatchImpl(Data
, Pointer
, Opts
);
79 void __ubsan::__ubsan_handle_type_mismatch_abort(TypeMismatchData
*Data
,
80 ValueHandle Pointer
) {
81 GET_REPORT_OPTIONS(true);
82 handleTypeMismatchImpl(Data
, Pointer
, Opts
);
86 /// \brief Common diagnostic emission for various forms of integer overflow.
88 static void handleIntegerOverflowImpl(OverflowData
*Data
, ValueHandle LHS
,
89 const char *Operator
, T RHS
,
91 SourceLocation Loc
= Data
->Loc
.acquire();
92 if (ignoreReport(Loc
, Opts
))
95 bool IsSigned
= Data
->Type
.isSignedIntegerTy();
96 ScopedReport
R(Opts
, Loc
, IsSigned
? ErrorType::SignedIntegerOverflow
97 : ErrorType::UnsignedIntegerOverflow
);
99 Diag(Loc
, DL_Error
, "%0 integer overflow: "
100 "%1 %2 %3 cannot be represented in type %4")
101 << (IsSigned
? "signed" : "unsigned")
102 << Value(Data
->Type
, LHS
) << Operator
<< RHS
<< Data
->Type
;
105 #define UBSAN_OVERFLOW_HANDLER(handler_name, op, abort) \
106 void __ubsan::handler_name(OverflowData *Data, ValueHandle LHS, \
108 GET_REPORT_OPTIONS(abort); \
109 handleIntegerOverflowImpl(Data, LHS, op, Value(Data->Type, RHS), Opts); \
113 UBSAN_OVERFLOW_HANDLER(__ubsan_handle_add_overflow
, "+", false)
114 UBSAN_OVERFLOW_HANDLER(__ubsan_handle_add_overflow_abort
, "+", true)
115 UBSAN_OVERFLOW_HANDLER(__ubsan_handle_sub_overflow
, "-", false)
116 UBSAN_OVERFLOW_HANDLER(__ubsan_handle_sub_overflow_abort
, "-", true)
117 UBSAN_OVERFLOW_HANDLER(__ubsan_handle_mul_overflow
, "*", false)
118 UBSAN_OVERFLOW_HANDLER(__ubsan_handle_mul_overflow_abort
, "*", true)
120 static void handleNegateOverflowImpl(OverflowData
*Data
, ValueHandle OldVal
,
121 ReportOptions Opts
) {
122 SourceLocation Loc
= Data
->Loc
.acquire();
123 if (ignoreReport(Loc
, Opts
))
126 bool IsSigned
= Data
->Type
.isSignedIntegerTy();
127 ScopedReport
R(Opts
, Loc
, IsSigned
? ErrorType::SignedIntegerOverflow
128 : ErrorType::UnsignedIntegerOverflow
);
132 "negation of %0 cannot be represented in type %1; "
133 "cast to an unsigned type to negate this value to itself")
134 << Value(Data
->Type
, OldVal
) << Data
->Type
;
136 Diag(Loc
, DL_Error
, "negation of %0 cannot be represented in type %1")
137 << Value(Data
->Type
, OldVal
) << Data
->Type
;
140 void __ubsan::__ubsan_handle_negate_overflow(OverflowData
*Data
,
141 ValueHandle OldVal
) {
142 GET_REPORT_OPTIONS(false);
143 handleNegateOverflowImpl(Data
, OldVal
, Opts
);
145 void __ubsan::__ubsan_handle_negate_overflow_abort(OverflowData
*Data
,
146 ValueHandle OldVal
) {
147 GET_REPORT_OPTIONS(true);
148 handleNegateOverflowImpl(Data
, OldVal
, Opts
);
152 static void handleDivremOverflowImpl(OverflowData
*Data
, ValueHandle LHS
,
153 ValueHandle RHS
, ReportOptions Opts
) {
154 SourceLocation Loc
= Data
->Loc
.acquire();
155 if (ignoreReport(Loc
, Opts
))
158 ScopedReport
R(Opts
, Loc
);
160 Value
LHSVal(Data
->Type
, LHS
);
161 Value
RHSVal(Data
->Type
, RHS
);
162 if (RHSVal
.isMinusOne()) {
163 R
.setErrorType(ErrorType::SignedIntegerOverflow
);
165 "division of %0 by -1 cannot be represented in type %1")
166 << LHSVal
<< Data
->Type
;
168 R
.setErrorType(Data
->Type
.isIntegerTy() ? ErrorType::IntegerDivideByZero
169 : ErrorType::FloatDivideByZero
);
170 Diag(Loc
, DL_Error
, "division by zero");
174 void __ubsan::__ubsan_handle_divrem_overflow(OverflowData
*Data
,
175 ValueHandle LHS
, ValueHandle RHS
) {
176 GET_REPORT_OPTIONS(false);
177 handleDivremOverflowImpl(Data
, LHS
, RHS
, Opts
);
179 void __ubsan::__ubsan_handle_divrem_overflow_abort(OverflowData
*Data
,
182 GET_REPORT_OPTIONS(true);
183 handleDivremOverflowImpl(Data
, LHS
, RHS
, Opts
);
187 static void handleShiftOutOfBoundsImpl(ShiftOutOfBoundsData
*Data
,
188 ValueHandle LHS
, ValueHandle RHS
,
189 ReportOptions Opts
) {
190 SourceLocation Loc
= Data
->Loc
.acquire();
191 if (ignoreReport(Loc
, Opts
))
194 ScopedReport
R(Opts
, Loc
);
196 Value
LHSVal(Data
->LHSType
, LHS
);
197 Value
RHSVal(Data
->RHSType
, RHS
);
198 if (RHSVal
.isNegative()) {
199 R
.setErrorType(ErrorType::InvalidShiftExponent
);
200 Diag(Loc
, DL_Error
, "shift exponent %0 is negative") << RHSVal
;
201 } else if (RHSVal
.getPositiveIntValue() >=
202 Data
->LHSType
.getIntegerBitWidth()) {
203 R
.setErrorType(ErrorType::InvalidShiftExponent
);
204 Diag(Loc
, DL_Error
, "shift exponent %0 is too large for %1-bit type %2")
205 << RHSVal
<< Data
->LHSType
.getIntegerBitWidth() << Data
->LHSType
;
206 } else if (LHSVal
.isNegative()) {
207 R
.setErrorType(ErrorType::InvalidShiftBase
);
208 Diag(Loc
, DL_Error
, "left shift of negative value %0") << LHSVal
;
210 R
.setErrorType(ErrorType::InvalidShiftBase
);
212 "left shift of %0 by %1 places cannot be represented in type %2")
213 << LHSVal
<< RHSVal
<< Data
->LHSType
;
217 void __ubsan::__ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData
*Data
,
220 GET_REPORT_OPTIONS(false);
221 handleShiftOutOfBoundsImpl(Data
, LHS
, RHS
, Opts
);
223 void __ubsan::__ubsan_handle_shift_out_of_bounds_abort(
224 ShiftOutOfBoundsData
*Data
,
227 GET_REPORT_OPTIONS(true);
228 handleShiftOutOfBoundsImpl(Data
, LHS
, RHS
, Opts
);
232 static void handleOutOfBoundsImpl(OutOfBoundsData
*Data
, ValueHandle Index
,
233 ReportOptions Opts
) {
234 SourceLocation Loc
= Data
->Loc
.acquire();
235 if (ignoreReport(Loc
, Opts
))
238 ScopedReport
R(Opts
, Loc
, ErrorType::OutOfBoundsIndex
);
240 Value
IndexVal(Data
->IndexType
, Index
);
241 Diag(Loc
, DL_Error
, "index %0 out of bounds for type %1")
242 << IndexVal
<< Data
->ArrayType
;
245 void __ubsan::__ubsan_handle_out_of_bounds(OutOfBoundsData
*Data
,
247 GET_REPORT_OPTIONS(false);
248 handleOutOfBoundsImpl(Data
, Index
, Opts
);
250 void __ubsan::__ubsan_handle_out_of_bounds_abort(OutOfBoundsData
*Data
,
252 GET_REPORT_OPTIONS(true);
253 handleOutOfBoundsImpl(Data
, Index
, Opts
);
257 static void handleBuiltinUnreachableImpl(UnreachableData
*Data
,
258 ReportOptions Opts
) {
259 ScopedReport
R(Opts
, Data
->Loc
, ErrorType::UnreachableCall
);
260 Diag(Data
->Loc
, DL_Error
, "execution reached a __builtin_unreachable() call");
263 void __ubsan::__ubsan_handle_builtin_unreachable(UnreachableData
*Data
) {
264 GET_REPORT_OPTIONS(true);
265 handleBuiltinUnreachableImpl(Data
, Opts
);
269 static void handleMissingReturnImpl(UnreachableData
*Data
, ReportOptions Opts
) {
270 ScopedReport
R(Opts
, Data
->Loc
, ErrorType::MissingReturn
);
271 Diag(Data
->Loc
, DL_Error
,
272 "execution reached the end of a value-returning function "
273 "without returning a value");
276 void __ubsan::__ubsan_handle_missing_return(UnreachableData
*Data
) {
277 GET_REPORT_OPTIONS(true);
278 handleMissingReturnImpl(Data
, Opts
);
282 static void handleVLABoundNotPositive(VLABoundData
*Data
, ValueHandle Bound
,
283 ReportOptions Opts
) {
284 SourceLocation Loc
= Data
->Loc
.acquire();
285 if (ignoreReport(Loc
, Opts
))
288 ScopedReport
R(Opts
, Loc
, ErrorType::NonPositiveVLAIndex
);
290 Diag(Loc
, DL_Error
, "variable length array bound evaluates to "
291 "non-positive value %0")
292 << Value(Data
->Type
, Bound
);
295 void __ubsan::__ubsan_handle_vla_bound_not_positive(VLABoundData
*Data
,
297 GET_REPORT_OPTIONS(false);
298 handleVLABoundNotPositive(Data
, Bound
, Opts
);
300 void __ubsan::__ubsan_handle_vla_bound_not_positive_abort(VLABoundData
*Data
,
302 GET_REPORT_OPTIONS(true);
303 handleVLABoundNotPositive(Data
, Bound
, Opts
);
307 static bool looksLikeFloatCastOverflowDataV1(void *Data
) {
308 // First field is either a pointer to filename or a pointer to a
310 u8
*FilenameOrTypeDescriptor
;
311 internal_memcpy(&FilenameOrTypeDescriptor
, Data
,
312 sizeof(FilenameOrTypeDescriptor
));
314 // Heuristic: For float_cast_overflow, the TypeKind will be either TK_Integer
315 // (0x0), TK_Float (0x1) or TK_Unknown (0xff). If both types are known,
316 // adding both bytes will be 0 or 1 (for BE or LE). If it were a filename,
317 // adding two printable characters will not yield such a value. Otherwise,
318 // if one of them is 0xff, this is most likely TK_Unknown type descriptor.
319 u16 MaybeFromTypeKind
=
320 FilenameOrTypeDescriptor
[0] + FilenameOrTypeDescriptor
[1];
321 return MaybeFromTypeKind
< 2 || FilenameOrTypeDescriptor
[0] == 0xff ||
322 FilenameOrTypeDescriptor
[1] == 0xff;
325 static void handleFloatCastOverflow(void *DataPtr
, ValueHandle From
,
326 ReportOptions Opts
) {
327 SymbolizedStackHolder CallerLoc
;
329 const TypeDescriptor
*FromType
, *ToType
;
331 if (looksLikeFloatCastOverflowDataV1(DataPtr
)) {
332 auto Data
= reinterpret_cast<FloatCastOverflowData
*>(DataPtr
);
333 CallerLoc
.reset(getCallerLocation(Opts
.pc
));
335 FromType
= &Data
->FromType
;
336 ToType
= &Data
->ToType
;
338 auto Data
= reinterpret_cast<FloatCastOverflowDataV2
*>(DataPtr
);
339 SourceLocation SLoc
= Data
->Loc
.acquire();
340 if (ignoreReport(SLoc
, Opts
))
343 FromType
= &Data
->FromType
;
344 ToType
= &Data
->ToType
;
347 ScopedReport
R(Opts
, Loc
, ErrorType::FloatCastOverflow
);
350 "value %0 is outside the range of representable values of type %2")
351 << Value(*FromType
, From
) << *FromType
<< *ToType
;
354 void __ubsan::__ubsan_handle_float_cast_overflow(void *Data
, ValueHandle From
) {
355 GET_REPORT_OPTIONS(false);
356 handleFloatCastOverflow(Data
, From
, Opts
);
358 void __ubsan::__ubsan_handle_float_cast_overflow_abort(void *Data
,
360 GET_REPORT_OPTIONS(true);
361 handleFloatCastOverflow(Data
, From
, Opts
);
365 static void handleLoadInvalidValue(InvalidValueData
*Data
, ValueHandle Val
,
366 ReportOptions Opts
) {
367 SourceLocation Loc
= Data
->Loc
.acquire();
368 if (ignoreReport(Loc
, Opts
))
371 // This check could be more precise if we used different handlers for
372 // -fsanitize=bool and -fsanitize=enum.
373 bool IsBool
= (0 == internal_strcmp(Data
->Type
.getTypeName(), "'bool'"));
374 ScopedReport
R(Opts
, Loc
, IsBool
? ErrorType::InvalidBoolLoad
375 : ErrorType::InvalidEnumLoad
);
378 "load of value %0, which is not a valid value for type %1")
379 << Value(Data
->Type
, Val
) << Data
->Type
;
382 void __ubsan::__ubsan_handle_load_invalid_value(InvalidValueData
*Data
,
384 GET_REPORT_OPTIONS(false);
385 handleLoadInvalidValue(Data
, Val
, Opts
);
387 void __ubsan::__ubsan_handle_load_invalid_value_abort(InvalidValueData
*Data
,
389 GET_REPORT_OPTIONS(true);
390 handleLoadInvalidValue(Data
, Val
, Opts
);
394 static void handleFunctionTypeMismatch(FunctionTypeMismatchData
*Data
,
395 ValueHandle Function
,
396 ReportOptions Opts
) {
397 SourceLocation CallLoc
= Data
->Loc
.acquire();
398 if (ignoreReport(CallLoc
, Opts
))
401 ScopedReport
R(Opts
, CallLoc
, ErrorType::FunctionTypeMismatch
);
403 SymbolizedStackHolder
FLoc(getSymbolizedLocation(Function
));
404 const char *FName
= FLoc
.get()->info
.function
;
408 Diag(CallLoc
, DL_Error
,
409 "call to function %0 through pointer to incorrect function type %1")
410 << FName
<< Data
->Type
;
411 Diag(FLoc
, DL_Note
, "%0 defined here") << FName
;
415 __ubsan::__ubsan_handle_function_type_mismatch(FunctionTypeMismatchData
*Data
,
416 ValueHandle Function
) {
417 GET_REPORT_OPTIONS(false);
418 handleFunctionTypeMismatch(Data
, Function
, Opts
);
421 void __ubsan::__ubsan_handle_function_type_mismatch_abort(
422 FunctionTypeMismatchData
*Data
, ValueHandle Function
) {
423 GET_REPORT_OPTIONS(true);
424 handleFunctionTypeMismatch(Data
, Function
, Opts
);
428 static void handleNonNullReturn(NonNullReturnData
*Data
, ReportOptions Opts
) {
429 SourceLocation Loc
= Data
->Loc
.acquire();
430 if (ignoreReport(Loc
, Opts
))
433 ScopedReport
R(Opts
, Loc
, ErrorType::InvalidNullReturn
);
435 Diag(Loc
, DL_Error
, "null pointer returned from function declared to never "
437 if (!Data
->AttrLoc
.isInvalid())
438 Diag(Data
->AttrLoc
, DL_Note
, "returns_nonnull attribute specified here");
441 void __ubsan::__ubsan_handle_nonnull_return(NonNullReturnData
*Data
) {
442 GET_REPORT_OPTIONS(false);
443 handleNonNullReturn(Data
, Opts
);
446 void __ubsan::__ubsan_handle_nonnull_return_abort(NonNullReturnData
*Data
) {
447 GET_REPORT_OPTIONS(true);
448 handleNonNullReturn(Data
, Opts
);
452 static void handleNonNullArg(NonNullArgData
*Data
, ReportOptions Opts
) {
453 SourceLocation Loc
= Data
->Loc
.acquire();
454 if (ignoreReport(Loc
, Opts
))
457 ScopedReport
R(Opts
, Loc
, ErrorType::InvalidNullArgument
);
459 Diag(Loc
, DL_Error
, "null pointer passed as argument %0, which is declared to "
460 "never be null") << Data
->ArgIndex
;
461 if (!Data
->AttrLoc
.isInvalid())
462 Diag(Data
->AttrLoc
, DL_Note
, "nonnull attribute specified here");
465 void __ubsan::__ubsan_handle_nonnull_arg(NonNullArgData
*Data
) {
466 GET_REPORT_OPTIONS(false);
467 handleNonNullArg(Data
, Opts
);
470 void __ubsan::__ubsan_handle_nonnull_arg_abort(NonNullArgData
*Data
) {
471 GET_REPORT_OPTIONS(true);
472 handleNonNullArg(Data
, Opts
);
476 static void handleCFIBadIcall(CFIBadIcallData
*Data
, ValueHandle Function
,
477 ReportOptions Opts
) {
478 SourceLocation Loc
= Data
->Loc
.acquire();
479 if (ignoreReport(Loc
, Opts
))
482 ScopedReport
R(Opts
, Loc
);
484 Diag(Loc
, DL_Error
, "control flow integrity check for type %0 failed during "
485 "indirect function call")
488 SymbolizedStackHolder
FLoc(getSymbolizedLocation(Function
));
489 const char *FName
= FLoc
.get()->info
.function
;
492 Diag(FLoc
, DL_Note
, "%0 defined here") << FName
;
495 void __ubsan::__ubsan_handle_cfi_bad_icall(CFIBadIcallData
*Data
,
496 ValueHandle Function
) {
497 GET_REPORT_OPTIONS(false);
498 handleCFIBadIcall(Data
, Function
, Opts
);
501 void __ubsan::__ubsan_handle_cfi_bad_icall_abort(CFIBadIcallData
*Data
,
502 ValueHandle Function
) {
503 GET_REPORT_OPTIONS(true);
504 handleCFIBadIcall(Data
, Function
, Opts
);
508 #endif // CAN_SANITIZE_UB