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