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
27using namespace __ubsan;
28
29typedef const char *(*ObjCGetClassNameTy)(void *);
30
31const 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
70SIntMax 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
91UIntMax 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
107UIntMax 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).
118FloatMax 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