| 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 | |