//===-- ubsan_handlers.cpp ------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Error logging entry points for the UBSan runtime. // //===----------------------------------------------------------------------===// // NOTE: Needs to be kept in sync with compiler-rt/lib/ubsan/ubsan_handlers.cpp // from LLVM project. #include "ubsan_handlers.h" #include "ubsan_diag.h" extern "C" __attribute__((noreturn)) void klee_report_error(const char *file, int line, const char *message, const char *suffix); namespace __ubsan { static const char *ConvertTypeToString(ErrorType Type) { switch (Type) { #define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) \ case ErrorType::Name: \ return SummaryKind; #include "ubsan_checks.inc" #undef UBSAN_CHECK } } __attribute__((noreturn)) static void report_error(const char *msg, const char *suffix) { klee_report_error(__FILE__, __LINE__, msg, suffix); } static const char *get_suffix(ErrorType ET) { switch (ET) { case ErrorType::GenericUB: // This ErrorType is only used in actual LLVM runtime // when `report_error_type` environment option is set to false. // It should never happen in KLEE runtime. return "exec.err"; case ErrorType::NullPointerUse: #if LLVM_VERSION_MAJOR >= 11 case ErrorType::NullPointerUseWithNullability: #endif #if LLVM_VERSION_MAJOR >= 10 case ErrorType::NullptrWithOffset: case ErrorType::NullptrWithNonZeroOffset: case ErrorType::NullptrAfterNonZeroOffset: #endif case ErrorType::PointerOverflow: case ErrorType::MisalignedPointerUse: case ErrorType::AlignmentAssumption: return "ptr.err"; case ErrorType::InsufficientObjectSize: // Convenient test has not been found in LLVM sources and therefore not been // added. return "ptr.err"; case ErrorType::SignedIntegerOverflow: case ErrorType::UnsignedIntegerOverflow: return "overflow.err"; case ErrorType::IntegerDivideByZero: case ErrorType::FloatDivideByZero: return "div.err"; case ErrorType::InvalidBuiltin: return "invalid_builtin_use.err"; #if LLVM_VERSION_MAJOR >= 11 case ErrorType::InvalidObjCCast: // Option `fsanitize=objc-cast` is not supported due to the requirement for // Darwin system. return "exec.err"; #endif case ErrorType::ImplicitUnsignedIntegerTruncation: case ErrorType::ImplicitSignedIntegerTruncation: return "implicit_truncation.err"; case ErrorType::ImplicitIntegerSignChange: case ErrorType::ImplicitSignedIntegerTruncationOrSignChange: return "implicit_conversion.err"; case ErrorType::InvalidShiftBase: case ErrorType::InvalidShiftExponent: return "overflow.err"; case ErrorType::OutOfBoundsIndex: return "ptr.err"; case ErrorType::UnreachableCall: return "unreachable_call.err"; case ErrorType::MissingReturn: return "missing_return.err"; case ErrorType::NonPositiveVLAIndex: return "ptr.err"; case ErrorType::FloatCastOverflow: return "overflow.err"; case ErrorType::InvalidBoolLoad: case ErrorType::InvalidEnumLoad: return "invalid_load.err"; case ErrorType::FunctionTypeMismatch: // This check is unsupported return "exec.err"; case ErrorType::InvalidNullReturn: #if LLVM_VERSION_MAJOR >= 11 case ErrorType::InvalidNullReturnWithNullability: #endif case ErrorType::InvalidNullArgument: #if LLVM_VERSION_MAJOR >= 11 case ErrorType::InvalidNullArgumentWithNullability: #endif return "nullable_attribute.err"; case ErrorType::DynamicTypeMismatch: case ErrorType::CFIBadType: // These checks are unsupported return "exec.err"; default: // In case something is not modelled return "exec.err"; } } __attribute__((noreturn)) static void report_error_type(ErrorType ET) { report_error(ConvertTypeToString(ET), get_suffix(ET)); } #if LLVM_VERSION_MAJOR >= 11 /// Situations in which we might emit a check for the suitability of a /// pointer or glvalue. Needs to be kept in sync with CodeGenFunction.h in /// clang. enum TypeCheckKind { /// Checking the operand of a load. Must be suitably sized and aligned. TCK_Load, /// Checking the destination of a store. Must be suitably sized and aligned. TCK_Store, /// Checking the bound value in a reference binding. Must be suitably sized /// and aligned, but is not required to refer to an object (until the /// reference is used), per core issue 453. TCK_ReferenceBinding, /// Checking the object expression in a non-static data member access. Must /// be an object within its lifetime. TCK_MemberAccess, /// Checking the 'this' pointer for a call to a non-static member function. /// Must be an object within its lifetime. TCK_MemberCall, /// Checking the 'this' pointer for a constructor call. TCK_ConstructorCall, /// Checking the operand of a static_cast to a derived pointer type. Must be /// null or an object within its lifetime. TCK_DowncastPointer, /// Checking the operand of a static_cast to a derived reference type. Must /// be an object within its lifetime. TCK_DowncastReference, /// Checking the operand of a cast to a base object. Must be suitably sized /// and aligned. TCK_Upcast, /// Checking the operand of a cast to a virtual base object. Must be an /// object within its lifetime. TCK_UpcastToVirtualBase, /// Checking the value assigned to a _Nonnull pointer. Must not be null. TCK_NonnullAssign, /// Checking the operand of a dynamic_cast or a typeid expression. Must be /// null or an object within its lifetime. TCK_DynamicOperation }; #endif static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer) { uptr Alignment = (uptr)1 << Data->LogAlignment; ErrorType ET; if (!Pointer) #if LLVM_VERSION_MAJOR >= 11 ET = (Data->TypeCheckKind == TCK_NonnullAssign) ? ErrorType::NullPointerUseWithNullability : ErrorType::NullPointerUse; #else ET = ErrorType::NullPointerUse; #endif else if (Pointer & (Alignment - 1)) ET = ErrorType::MisalignedPointerUse; else ET = ErrorType::InsufficientObjectSize; report_error_type(ET); } extern "C" void __ubsan_handle_type_mismatch_v1(TypeMismatchData *Data, ValueHandle Pointer) { handleTypeMismatchImpl(Data, Pointer); } extern "C" void __ubsan_handle_type_mismatch_v1_abort(TypeMismatchData *Data, ValueHandle Pointer) { handleTypeMismatchImpl(Data, Pointer); } static void handleAlignmentAssumptionImpl(AlignmentAssumptionData * /*Data*/, ValueHandle /*Pointer*/, ValueHandle /*Alignment*/, ValueHandle /*Offset*/) { ErrorType ET = ErrorType::AlignmentAssumption; report_error_type(ET); } extern "C" void __ubsan_handle_alignment_assumption(AlignmentAssumptionData *Data, ValueHandle Pointer, ValueHandle Alignment, ValueHandle Offset) { handleAlignmentAssumptionImpl(Data, Pointer, Alignment, Offset); } extern "C" void __ubsan_handle_alignment_assumption_abort( AlignmentAssumptionData *Data, ValueHandle Pointer, ValueHandle Alignment, ValueHandle Offset) { handleAlignmentAssumptionImpl(Data, Pointer, Alignment, Offset); } /// \brief Common diagnostic emission for various forms of integer overflow. static void handleIntegerOverflowImpl(OverflowData *Data, ValueHandle /*LHS*/, const char * /*Operator*/) { bool IsSigned = Data->Type.isSignedIntegerTy(); ErrorType ET = IsSigned ? ErrorType::SignedIntegerOverflow : ErrorType::UnsignedIntegerOverflow; report_error_type(ET); } #define UBSAN_OVERFLOW_HANDLER(handler_name, op, unrecoverable) \ extern "C" void handler_name(OverflowData *Data, ValueHandle LHS, \ ValueHandle RHS) { \ handleIntegerOverflowImpl(Data, LHS, op); \ } UBSAN_OVERFLOW_HANDLER(__ubsan_handle_add_overflow, "+", false) UBSAN_OVERFLOW_HANDLER(__ubsan_handle_add_overflow_abort, "+", true) UBSAN_OVERFLOW_HANDLER(__ubsan_handle_sub_overflow, "-", false) UBSAN_OVERFLOW_HANDLER(__ubsan_handle_sub_overflow_abort, "-", true) UBSAN_OVERFLOW_HANDLER(__ubsan_handle_mul_overflow, "*", false) UBSAN_OVERFLOW_HANDLER(__ubsan_handle_mul_overflow_abort, "*", true) static void handleNegateOverflowImpl(OverflowData *Data, ValueHandle /*OldVal*/) { bool IsSigned = Data->Type.isSignedIntegerTy(); ErrorType ET = IsSigned ? ErrorType::SignedIntegerOverflow : ErrorType::UnsignedIntegerOverflow; report_error_type(ET); } extern "C" void __ubsan_handle_negate_overflow(OverflowData *Data, ValueHandle OldVal) { handleNegateOverflowImpl(Data, OldVal); } extern "C" void __ubsan_handle_negate_overflow_abort(OverflowData *Data, ValueHandle OldVal) { handleNegateOverflowImpl(Data, OldVal); } static void handleDivremOverflowImpl(OverflowData *Data, ValueHandle /*LHS*/, ValueHandle /*RHS*/) { if (Data->Type.isIntegerTy()) report_error("integer division overflow", "overflow.err"); else { ErrorType ET = ErrorType::FloatDivideByZero; report_error_type(ET); } } extern "C" void __ubsan_handle_divrem_overflow(OverflowData *Data, ValueHandle LHS, ValueHandle RHS) { handleDivremOverflowImpl(Data, LHS, RHS); } extern "C" void __ubsan_handle_divrem_overflow_abort(OverflowData *Data, ValueHandle LHS, ValueHandle RHS) { handleDivremOverflowImpl(Data, LHS, RHS); } static void handleShiftOutOfBoundsImpl(ShiftOutOfBoundsData * /*Data*/, ValueHandle /*LHS*/, ValueHandle /*RHS*/) { report_error("shift out of bounds", "overflow.err"); } extern "C" void __ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData *Data, ValueHandle LHS, ValueHandle RHS) { handleShiftOutOfBoundsImpl(Data, LHS, RHS); } extern "C" void __ubsan_handle_shift_out_of_bounds_abort(ShiftOutOfBoundsData *Data, ValueHandle LHS, ValueHandle RHS) { handleShiftOutOfBoundsImpl(Data, LHS, RHS); } static void handleOutOfBoundsImpl(OutOfBoundsData * /*Data*/, ValueHandle /*Index*/) { ErrorType ET = ErrorType::OutOfBoundsIndex; report_error_type(ET); } extern "C" void __ubsan_handle_out_of_bounds(OutOfBoundsData *Data, ValueHandle Index) { handleOutOfBoundsImpl(Data, Index); } extern "C" void __ubsan_handle_out_of_bounds_abort(OutOfBoundsData *Data, ValueHandle Index) { handleOutOfBoundsImpl(Data, Index); } static void handleBuiltinUnreachableImpl(UnreachableData * /*Data*/) { ErrorType ET = ErrorType::UnreachableCall; report_error_type(ET); } extern "C" void __ubsan_handle_builtin_unreachable(UnreachableData *Data) { handleBuiltinUnreachableImpl(Data); } static void handleMissingReturnImpl(UnreachableData * /*Data*/) { ErrorType ET = ErrorType::MissingReturn; report_error_type(ET); } extern "C" void __ubsan_handle_missing_return(UnreachableData *Data) { handleMissingReturnImpl(Data); } static void handleVLABoundNotPositive(VLABoundData * /*Data*/, ValueHandle /*Bound*/) { ErrorType ET = ErrorType::NonPositiveVLAIndex; report_error_type(ET); } extern "C" void __ubsan_handle_vla_bound_not_positive(VLABoundData *Data, ValueHandle Bound) { handleVLABoundNotPositive(Data, Bound); } extern "C" void __ubsan_handle_vla_bound_not_positive_abort(VLABoundData *Data, ValueHandle Bound) { handleVLABoundNotPositive(Data, Bound); } static void handleFloatCastOverflow(void * /*DataPtr*/, ValueHandle /*From*/) { ErrorType ET = ErrorType::FloatCastOverflow; report_error_type(ET); } extern "C" void __ubsan_handle_float_cast_overflow(void *Data, ValueHandle From) { handleFloatCastOverflow(Data, From); } extern "C" void __ubsan_handle_float_cast_overflow_abort(void *Data, ValueHandle From) { handleFloatCastOverflow(Data, From); } static void handleLoadInvalidValue(InvalidValueData * /*Data*/, ValueHandle /*Val*/) { report_error("load invalid value", "invalid_load.err"); } extern "C" void __ubsan_handle_load_invalid_value(InvalidValueData *Data, ValueHandle Val) { handleLoadInvalidValue(Data, Val); } extern "C" void __ubsan_handle_load_invalid_value_abort(InvalidValueData *Data, ValueHandle Val) { handleLoadInvalidValue(Data, Val); } static void handleImplicitConversion(ImplicitConversionData *Data, ValueHandle /*Src*/, ValueHandle /*Dst*/) { ErrorType ET = ErrorType::GenericUB; const TypeDescriptor &SrcTy = Data->FromType; const TypeDescriptor &DstTy = Data->ToType; bool SrcSigned = SrcTy.isSignedIntegerTy(); bool DstSigned = DstTy.isSignedIntegerTy(); switch (Data->Kind) { case ICCK_IntegerTruncation: { // Legacy, no longer used. // Let's figure out what it should be as per the new types, and upgrade. // If both types are unsigned, then it's an unsigned truncation. // Else, it is a signed truncation. if (!SrcSigned && !DstSigned) { ET = ErrorType::ImplicitUnsignedIntegerTruncation; } else { ET = ErrorType::ImplicitSignedIntegerTruncation; } break; } case ICCK_UnsignedIntegerTruncation: ET = ErrorType::ImplicitUnsignedIntegerTruncation; break; case ICCK_SignedIntegerTruncation: ET = ErrorType::ImplicitSignedIntegerTruncation; break; case ICCK_IntegerSignChange: ET = ErrorType::ImplicitIntegerSignChange; break; case ICCK_SignedIntegerTruncationOrSignChange: ET = ErrorType::ImplicitSignedIntegerTruncationOrSignChange; break; } report_error_type(ET); } extern "C" void __ubsan_handle_implicit_conversion(ImplicitConversionData *Data, ValueHandle Src, ValueHandle Dst) { handleImplicitConversion(Data, Src, Dst); } extern "C" void __ubsan_handle_implicit_conversion_abort(ImplicitConversionData *Data, ValueHandle Src, ValueHandle Dst) { handleImplicitConversion(Data, Src, Dst); } static void handleInvalidBuiltin(InvalidBuiltinData * /*Data*/) { ErrorType ET = ErrorType::InvalidBuiltin; report_error_type(ET); } extern "C" void __ubsan_handle_invalid_builtin(InvalidBuiltinData *Data) { handleInvalidBuiltin(Data); } extern "C" void __ubsan_handle_invalid_builtin_abort(InvalidBuiltinData *Data) { handleInvalidBuiltin(Data); } static void handleNonNullReturn(NonNullReturnData * /*Data*/, SourceLocation * /*LocPtr*/, bool IsAttr) { #if LLVM_VERSION_MAJOR >= 11 ErrorType ET = IsAttr ? ErrorType::InvalidNullReturn : ErrorType::InvalidNullReturnWithNullability; #else ErrorType ET = ErrorType::InvalidNullReturn; #endif report_error_type(ET); } extern "C" void __ubsan_handle_nonnull_return_v1(NonNullReturnData *Data, SourceLocation *LocPtr) { handleNonNullReturn(Data, LocPtr, true); } extern "C" void __ubsan_handle_nonnull_return_v1_abort(NonNullReturnData *Data, SourceLocation *LocPtr) { handleNonNullReturn(Data, LocPtr, true); } extern "C" void __ubsan_handle_nullability_return_v1(NonNullReturnData *Data, SourceLocation *LocPtr) { handleNonNullReturn(Data, LocPtr, false); } extern "C" void __ubsan_handle_nullability_return_v1_abort(NonNullReturnData *Data, SourceLocation *LocPtr) { handleNonNullReturn(Data, LocPtr, false); } static void handleNonNullArg(NonNullArgData * /*Data*/, bool IsAttr) { #if LLVM_VERSION_MAJOR >= 11 ErrorType ET = IsAttr ? ErrorType::InvalidNullArgument : ErrorType::InvalidNullArgumentWithNullability; #else ErrorType ET = ErrorType::InvalidNullArgument; #endif report_error_type(ET); } extern "C" void __ubsan_handle_nonnull_arg(NonNullArgData *Data) { handleNonNullArg(Data, true); } extern "C" void __ubsan_handle_nonnull_arg_abort(NonNullArgData *Data) { handleNonNullArg(Data, true); } extern "C" void __ubsan_handle_nullability_arg(NonNullArgData *Data) { handleNonNullArg(Data, false); } extern "C" void __ubsan_handle_nullability_arg_abort(NonNullArgData *Data) { handleNonNullArg(Data, false); } static void handlePointerOverflowImpl(PointerOverflowData * /*Data*/, ValueHandle Base, ValueHandle Result) { #if LLVM_VERSION_MAJOR >= 10 ErrorType ET; if (Base == 0 && Result == 0) ET = ErrorType::NullptrWithOffset; else if (Base == 0 && Result != 0) ET = ErrorType::NullptrWithNonZeroOffset; else if (Base != 0 && Result == 0) ET = ErrorType::NullptrAfterNonZeroOffset; else ET = ErrorType::PointerOverflow; #else ErrorType ET = ErrorType::PointerOverflow; #endif report_error_type(ET); } extern "C" void __ubsan_handle_pointer_overflow(PointerOverflowData *Data, ValueHandle Base, ValueHandle Result) { handlePointerOverflowImpl(Data, Base, Result); } extern "C" void __ubsan_handle_pointer_overflow_abort(PointerOverflowData *Data, ValueHandle Base, ValueHandle Result) { handlePointerOverflowImpl(Data, Base, Result); } } // namespace __ubsan