| 1 | //===-- ubsan_handlers_cxx.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, which are only used for C++ |
| 10 | // compilations. This file is permitted to use language features which require |
| 11 | // linking against a C++ ABI library. |
| 12 | // |
| 13 | //===----------------------------------------------------------------------===// |
| 14 | |
| 15 | #include "ubsan_platform.h" |
| 16 | #if CAN_SANITIZE_UB |
| 17 | #include "ubsan_handlers.h" |
| 18 | #include "ubsan_handlers_cxx.h" |
| 19 | #include "ubsan_diag.h" |
| 20 | #include "ubsan_type_hash.h" |
| 21 | |
| 22 | #include "sanitizer_common/sanitizer_common.h" |
| 23 | #include "sanitizer_common/sanitizer_suppressions.h" |
| 24 | |
| 25 | using namespace __sanitizer; |
| 26 | using namespace __ubsan; |
| 27 | |
| 28 | namespace __ubsan { |
| 29 | extern const char *const TypeCheckKinds[]; |
| 30 | } |
| 31 | |
| 32 | // Returns true if UBSan has printed an error report. |
| 33 | static bool HandleDynamicTypeCacheMiss( |
| 34 | DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash, |
| 35 | ReportOptions Opts) { |
| 36 | if (checkDynamicType(Object: (void*)Pointer, Type: Data->TypeInfo, Hash)) |
| 37 | // Just a cache miss. The type matches after all. |
| 38 | return false; |
| 39 | |
| 40 | // Check if error report should be suppressed. |
| 41 | DynamicTypeInfo DTI = getDynamicTypeInfoFromObject(Object: (void*)Pointer); |
| 42 | if (DTI.isValid() && IsVptrCheckSuppressed(TypeName: DTI.getMostDerivedTypeName())) |
| 43 | return false; |
| 44 | |
| 45 | SourceLocation Loc = Data->Loc.acquire(); |
| 46 | ErrorType ET = ErrorType::DynamicTypeMismatch; |
| 47 | if (ignoreReport(SLoc: Loc, Opts, ET)) |
| 48 | return false; |
| 49 | |
| 50 | ScopedReport R(Opts, Loc, ET); |
| 51 | |
| 52 | Diag(Loc, DL_Error, ET, |
| 53 | "%0 address %1 which does not point to an object of type %2" ) |
| 54 | << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type; |
| 55 | |
| 56 | // If possible, say what type it actually points to. |
| 57 | if (!DTI.isValid()) { |
| 58 | if (DTI.getOffset() < -VptrMaxOffsetToTop || DTI.getOffset() > VptrMaxOffsetToTop) { |
| 59 | Diag(Pointer, DL_Note, ET, |
| 60 | "object has a possibly invalid vptr: abs(offset to top) too big" ) |
| 61 | << TypeName(DTI.getMostDerivedTypeName()) |
| 62 | << Range(Pointer, Pointer + sizeof(uptr), "possibly invalid vptr" ); |
| 63 | } else { |
| 64 | Diag(Pointer, DL_Note, ET, "object has invalid vptr" ) |
| 65 | << TypeName(DTI.getMostDerivedTypeName()) |
| 66 | << Range(Pointer, Pointer + sizeof(uptr), "invalid vptr" ); |
| 67 | } |
| 68 | } else if (!DTI.getOffset()) |
| 69 | Diag(Pointer, DL_Note, ET, "object is of type %0" ) |
| 70 | << TypeName(DTI.getMostDerivedTypeName()) |
| 71 | << Range(Pointer, Pointer + sizeof(uptr), "vptr for %0" ); |
| 72 | else |
| 73 | // FIXME: Find the type at the specified offset, and include that |
| 74 | // in the note. |
| 75 | Diag(Pointer - DTI.getOffset(), DL_Note, ET, |
| 76 | "object is base class subobject at offset %0 within object of type %1" ) |
| 77 | << DTI.getOffset() << TypeName(DTI.getMostDerivedTypeName()) |
| 78 | << TypeName(DTI.getSubobjectTypeName()) |
| 79 | << Range(Pointer, Pointer + sizeof(uptr), |
| 80 | "vptr for %2 base class of %1" ); |
| 81 | return true; |
| 82 | } |
| 83 | |
| 84 | void __ubsan::__ubsan_handle_dynamic_type_cache_miss( |
| 85 | DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash) { |
| 86 | GET_REPORT_OPTIONS(false); |
| 87 | HandleDynamicTypeCacheMiss(Data, Pointer, Hash, Opts); |
| 88 | } |
| 89 | void __ubsan::__ubsan_handle_dynamic_type_cache_miss_abort( |
| 90 | DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash) { |
| 91 | // Note: -fsanitize=vptr is always recoverable. |
| 92 | GET_REPORT_OPTIONS(false); |
| 93 | if (HandleDynamicTypeCacheMiss(Data, Pointer, Hash, Opts)) |
| 94 | Die(); |
| 95 | } |
| 96 | |
| 97 | namespace __ubsan { |
| 98 | void __ubsan_handle_cfi_bad_type(CFICheckFailData *Data, ValueHandle Vtable, |
| 99 | bool ValidVtable, ReportOptions Opts) { |
| 100 | SourceLocation Loc = Data->Loc.acquire(); |
| 101 | ErrorType ET = ErrorType::CFIBadType; |
| 102 | |
| 103 | if (ignoreReport(SLoc: Loc, Opts, ET)) |
| 104 | return; |
| 105 | |
| 106 | ScopedReport R(Opts, Loc, ET); |
| 107 | DynamicTypeInfo DTI = ValidVtable |
| 108 | ? getDynamicTypeInfoFromVtable(Vtable: (void *)Vtable) |
| 109 | : DynamicTypeInfo(0, 0, 0); |
| 110 | |
| 111 | const char *CheckKindStr; |
| 112 | switch (Data->CheckKind) { |
| 113 | case CFITCK_VCall: |
| 114 | CheckKindStr = "virtual call" ; |
| 115 | break; |
| 116 | case CFITCK_NVCall: |
| 117 | CheckKindStr = "non-virtual call" ; |
| 118 | break; |
| 119 | case CFITCK_DerivedCast: |
| 120 | CheckKindStr = "base-to-derived cast" ; |
| 121 | break; |
| 122 | case CFITCK_UnrelatedCast: |
| 123 | CheckKindStr = "cast to unrelated type" ; |
| 124 | break; |
| 125 | case CFITCK_VMFCall: |
| 126 | CheckKindStr = "virtual pointer to member function call" ; |
| 127 | break; |
| 128 | case CFITCK_ICall: |
| 129 | case CFITCK_NVMFCall: |
| 130 | Die(); |
| 131 | } |
| 132 | |
| 133 | Diag(Loc, DL_Error, ET, |
| 134 | "control flow integrity check for type %0 failed during " |
| 135 | "%1 (vtable address %2)" ) |
| 136 | << Data->Type << CheckKindStr << (void *)Vtable; |
| 137 | |
| 138 | // If possible, say what type it actually points to. |
| 139 | if (!DTI.isValid()) |
| 140 | Diag(Vtable, DL_Note, ET, "invalid vtable" ); |
| 141 | else |
| 142 | Diag(Vtable, DL_Note, ET, "vtable is of type %0" ) |
| 143 | << TypeName(DTI.getMostDerivedTypeName()); |
| 144 | |
| 145 | // If the failure involved different DSOs for the check location and vtable, |
| 146 | // report the DSO names. |
| 147 | const char *DstModule = Symbolizer::GetOrInit()->GetModuleNameForPc(pc: Vtable); |
| 148 | if (!DstModule) |
| 149 | DstModule = "(unknown)" ; |
| 150 | |
| 151 | const char *SrcModule = Symbolizer::GetOrInit()->GetModuleNameForPc(pc: Opts.pc); |
| 152 | if (!SrcModule) |
| 153 | SrcModule = "(unknown)" ; |
| 154 | |
| 155 | if (internal_strcmp(s1: SrcModule, s2: DstModule)) |
| 156 | Diag(Loc, DL_Note, ET, "check failed in %0, vtable located in %1" ) |
| 157 | << SrcModule << DstModule; |
| 158 | } |
| 159 | } // namespace __ubsan |
| 160 | |
| 161 | #endif // CAN_SANITIZE_UB |
| 162 | |