1 //===-- ubsan_value.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 // Representation of a runtime value, as marshaled from the generated code to 10 // the ubsan runtime. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "ubsan_platform.h" 15 #if CAN_SANITIZE_UB 16 #include "ubsan_value.h" 17 #include "sanitizer_common/sanitizer_common.h" 18 #include "sanitizer_common/sanitizer_libc.h" 19 #include "sanitizer_common/sanitizer_mutex.h" 20 21 #if SANITIZER_APPLE 22 #include <dlfcn.h> 23 #endif 24 25 using namespace __ubsan; 26 27 typedef const char *(*ObjCGetClassNameTy)(void *); 28 29 const char *__ubsan::getObjCClassName(ValueHandle Pointer) { 30 #if SANITIZER_APPLE 31 // We need to query the ObjC runtime for some information, but do not want 32 // to introduce a static dependency from the ubsan runtime onto ObjC. Try to 33 // grab a handle to the ObjC runtime used by the process. 34 static bool AttemptedDlopen = false; 35 static void *ObjCHandle = nullptr; 36 static void *ObjCObjectGetClassName = nullptr; 37 38 // Prevent threads from racing to dlopen(). 39 static __sanitizer::StaticSpinMutex Lock; 40 { 41 __sanitizer::SpinMutexLock Guard(&Lock); 42 43 if (!AttemptedDlopen) { 44 ObjCHandle = dlopen( 45 "/usr/lib/libobjc.A.dylib", 46 RTLD_LAZY // Only bind symbols when used. 47 | RTLD_LOCAL // Only make symbols available via the handle. 48 | RTLD_NOLOAD // Do not load the dylib, just grab a handle if the 49 // image is already loaded. 50 | RTLD_FIRST // Only search the image pointed-to by the handle. 51 ); 52 AttemptedDlopen = true; 53 if (!ObjCHandle) 54 return nullptr; 55 ObjCObjectGetClassName = dlsym(ObjCHandle, "object_getClassName"); 56 } 57 } 58 59 if (!ObjCObjectGetClassName) 60 return nullptr; 61 62 return ObjCGetClassNameTy(ObjCObjectGetClassName)((void *)Pointer); 63 #else 64 return nullptr; 65 #endif 66 } 67 68 SIntMax Value::getSIntValue() const { 69 CHECK(getType().isSignedIntegerTy()); 70 if (isInlineInt()) { 71 // Val was zero-extended to ValueHandle. Sign-extend from original width 72 // to SIntMax. 73 const unsigned ExtraBits = 74 sizeof(SIntMax) * 8 - getType().getIntegerBitWidth(); 75 return SIntMax(UIntMax(Val) << ExtraBits) >> ExtraBits; 76 } 77 if (getType().getIntegerBitWidth() == 64) 78 return *reinterpret_cast<s64*>(Val); 79 #if HAVE_INT128_T 80 if (getType().getIntegerBitWidth() == 128) 81 return *reinterpret_cast<s128*>(Val); 82 #else 83 if (getType().getIntegerBitWidth() == 128) 84 UNREACHABLE("libclang_rt.ubsan was built without __int128 support"); 85 #endif 86 UNREACHABLE("unexpected bit width"); 87 } 88 89 UIntMax Value::getUIntValue() const { 90 CHECK(getType().isUnsignedIntegerTy()); 91 if (isInlineInt()) 92 return Val; 93 if (getType().getIntegerBitWidth() == 64) 94 return *reinterpret_cast<u64*>(Val); 95 #if HAVE_INT128_T 96 if (getType().getIntegerBitWidth() == 128) 97 return *reinterpret_cast<u128*>(Val); 98 #else 99 if (getType().getIntegerBitWidth() == 128) 100 UNREACHABLE("libclang_rt.ubsan was built without __int128 support"); 101 #endif 102 UNREACHABLE("unexpected bit width"); 103 } 104 105 UIntMax Value::getPositiveIntValue() const { 106 if (getType().isUnsignedIntegerTy()) 107 return getUIntValue(); 108 SIntMax Val = getSIntValue(); 109 CHECK(Val >= 0); 110 return Val; 111 } 112 113 /// Get the floating-point value of this object, extended to a long double. 114 /// These are always passed by address (our calling convention doesn't allow 115 /// them to be passed in floating-point registers, so this has little cost). 116 FloatMax Value::getFloatValue() const { 117 CHECK(getType().isFloatTy()); 118 if (isInlineFloat()) { 119 switch (getType().getFloatBitWidth()) { 120 #if 0 121 // FIXME: OpenCL / NEON 'half' type. LLVM can't lower the conversion 122 // from '__fp16' to 'long double'. 123 case 16: { 124 __fp16 Value; 125 internal_memcpy(&Value, &Val, 4); 126 return Value; 127 } 128 #endif 129 case 32: { 130 float Value; 131 #if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 132 // For big endian the float value is in the last 4 bytes. 133 // On some targets we may only have 4 bytes so we count backwards from 134 // the end of Val to account for both the 32-bit and 64-bit cases. 135 internal_memcpy(&Value, ((const char*)(&Val + 1)) - 4, 4); 136 #else 137 internal_memcpy(&Value, &Val, 4); 138 #endif 139 return Value; 140 } 141 case 64: { 142 double Value; 143 internal_memcpy(&Value, &Val, 8); 144 return Value; 145 } 146 } 147 } else { 148 switch (getType().getFloatBitWidth()) { 149 case 64: return *reinterpret_cast<double*>(Val); 150 case 80: return *reinterpret_cast<long double*>(Val); 151 case 96: return *reinterpret_cast<long double*>(Val); 152 case 128: return *reinterpret_cast<long double*>(Val); 153 } 154 } 155 UNREACHABLE("unexpected floating point bit width"); 156 } 157 158 #endif // CAN_SANITIZE_UB 159