[llvm-commits] [compiler-rt] r166660 - in /compiler-rt/trunk/lib/ubsan: CMakeLists.txt lit_tests/TypeCheck/vptr.cpp ubsan_handlers.cc ubsan_handlers_cxx.cc ubsan_handlers_cxx.h ubsan_type_hash.cc ubsan_type_hash.h

Richard Smith richard-llvm at metafoo.co.uk
Wed Oct 24 19:07:02 PDT 2012


Author: rsmith
Date: Wed Oct 24 21:07:02 2012
New Revision: 166660

URL: http://llvm.org/viewvc/llvm-project?rev=166660&view=rev
Log:
-fcatch-undefined-behavior checking for appropriate vptr value: library side.

Added:
    compiler-rt/trunk/lib/ubsan/lit_tests/TypeCheck/vptr.cpp
    compiler-rt/trunk/lib/ubsan/ubsan_handlers_cxx.cc
    compiler-rt/trunk/lib/ubsan/ubsan_handlers_cxx.h
    compiler-rt/trunk/lib/ubsan/ubsan_type_hash.cc
    compiler-rt/trunk/lib/ubsan/ubsan_type_hash.h
Modified:
    compiler-rt/trunk/lib/ubsan/CMakeLists.txt
    compiler-rt/trunk/lib/ubsan/ubsan_handlers.cc

Modified: compiler-rt/trunk/lib/ubsan/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/ubsan/CMakeLists.txt?rev=166660&r1=166659&r2=166660&view=diff
==============================================================================
--- compiler-rt/trunk/lib/ubsan/CMakeLists.txt (original)
+++ compiler-rt/trunk/lib/ubsan/CMakeLists.txt Wed Oct 24 21:07:02 2012
@@ -3,6 +3,8 @@
 set(UBSAN_SOURCES
   ubsan_diag.cc
   ubsan_handlers.cc
+  ubsan_handlers_cxx.cc
+  ubsan_type_hash.cc
   ubsan_value.cc
   )
 

Added: compiler-rt/trunk/lib/ubsan/lit_tests/TypeCheck/vptr.cpp
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/ubsan/lit_tests/TypeCheck/vptr.cpp?rev=166660&view=auto
==============================================================================
--- compiler-rt/trunk/lib/ubsan/lit_tests/TypeCheck/vptr.cpp (added)
+++ compiler-rt/trunk/lib/ubsan/lit_tests/TypeCheck/vptr.cpp Wed Oct 24 21:07:02 2012
@@ -0,0 +1,74 @@
+// RUN: %clang -ccc-cxx -fcatch-undefined-behavior %s -O3 -o %t
+// RUN: %t rT && %t mT && %t fT
+// RUN: %t rU && %t mU && %t fU
+// RUN: %t rS 2>&1 | FileCheck %s --check-prefix=CHECK-REFERENCE
+// RUN: %t mS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER
+// RUN: %t fS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN
+// RUN: %t rV 2>&1 | FileCheck %s --check-prefix=CHECK-REFERENCE
+// RUN: %t mV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER
+// RUN: %t fV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN
+
+struct S {
+  S() : a(0) {}
+  ~S() {}
+  int a;
+  int f() { return 0; }
+  virtual int v() { return 0; }
+};
+
+struct T : S {
+  T() : b(0) {}
+  int b;
+  int g() { return 0; }
+  virtual int v() { return 1; }
+};
+
+struct U : S, T { virtual int v() { return 2; } };
+
+int main(int, char **argv) {
+  T t;
+  (void)t.a;
+  (void)t.b;
+  (void)t.f();
+  (void)t.g();
+  (void)t.v();
+  (void)t.S::v();
+
+  U u;
+  (void)u.T::a;
+  (void)u.b;
+  (void)u.T::f();
+  (void)u.g();
+  (void)u.v();
+  (void)u.T::v();
+  (void)((T&)u).S::v();
+
+  T *p = 0;
+  switch (argv[1][1]) {
+  case 'S':
+    p = reinterpret_cast<T*>(new S);
+    break;
+  case 'T':
+    p = new T;
+    break;
+  case 'U':
+    p = new U;
+    break;
+  case 'V':
+    p = reinterpret_cast<T*>(new U);
+    break;
+  }
+
+  switch (argv[1][0]) {
+  case 'r':
+    // CHECK-REFERENCE: vptr.cpp:65:13: fatal error: reference binding to address 0x{{[0-9a-f]*}} which does not point to an object of type 'T'
+    {T &r = *p;}
+    break;
+  case 'm':
+    // CHECK-MEMBER: vptr.cpp:69:15: fatal error: member access within address 0x{{[0-9a-f]*}} which does not point to an object of type 'T'
+    return p->b;
+  case 'f':
+    // CHECK-MEMFUN: vptr.cpp:72:12: fatal error: member call on address 0x{{[0-9a-f]*}} which does not point to an object of type 'T'
+    return p->g();
+  }
+}

Modified: compiler-rt/trunk/lib/ubsan/ubsan_handlers.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/ubsan/ubsan_handlers.cc?rev=166660&r1=166659&r2=166660&view=diff
==============================================================================
--- compiler-rt/trunk/lib/ubsan/ubsan_handlers.cc (original)
+++ compiler-rt/trunk/lib/ubsan/ubsan_handlers.cc Wed Oct 24 21:07:02 2012
@@ -1,4 +1,4 @@
-//===-- ubsan_report.cc ---------------------------------------------------===//
+//===-- ubsan_handlers.cc -------------------------------------------------===//
 //
 //                     The LLVM Compiler Infrastructure
 //
@@ -31,12 +31,15 @@
   Die();
 }
 
-void __ubsan::__ubsan_handle_type_mismatch(TypeMismatchData *Data,
-                                           ValueHandle Pointer) {
+namespace __ubsan {
   const char *TypeCheckKinds[] = {
     "load of", "store to", "reference binding to", "member access within",
-    "member call on"
+    "member call on", "constructor call on"
   };
+}
+
+void __ubsan::__ubsan_handle_type_mismatch(TypeMismatchData *Data,
+                                           ValueHandle Pointer) {
   if (!Pointer)
     Diag(Data->Loc, "%0 null pointer of type %1")
       << TypeCheckKinds[Data->TypeCheckKind] << Data->Type;

Added: compiler-rt/trunk/lib/ubsan/ubsan_handlers_cxx.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/ubsan/ubsan_handlers_cxx.cc?rev=166660&view=auto
==============================================================================
--- compiler-rt/trunk/lib/ubsan/ubsan_handlers_cxx.cc (added)
+++ compiler-rt/trunk/lib/ubsan/ubsan_handlers_cxx.cc Wed Oct 24 21:07:02 2012
@@ -0,0 +1,49 @@
+//===-- ubsan_handlers_cxx.cc ---------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Error logging entry points for the UBSan runtime, which are only used for C++
+// compilations. This file is permitted to use language features which require
+// linking against a C++ ABI library.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_handlers_cxx.h"
+#include "ubsan_diag.h"
+#include "ubsan_type_hash.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+
+using namespace __sanitizer;
+using namespace __ubsan;
+
+namespace __ubsan {
+  extern const char *TypeCheckKinds[];
+}
+
+void __ubsan::__ubsan_handle_dynamic_type_cache_miss(
+  DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash) {
+  if (checkDynamicType((void*)Pointer, Data->TypeInfo, Hash))
+    // Just a cache miss. The type matches after all.
+    return;
+
+  Diag(Data->Loc, "%0 address %1 which does not point to an object of type %2")
+    << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type;
+  // FIXME: If possible, say what type it actually points to. Produce a note
+  //        pointing out the vptr:
+  // lib/VMCore/Instructions.cpp:2020:10: fatal error: member call on address
+  //       0xb7a4440 which does not point to an object of type
+  //       'llvm::OverflowingBinaryOperator'
+  //   return cast<OverflowingBinaryOperator>(this)->hasNoSignedWrap();
+  //                                               ^
+  // 0xb7a4440: note: object is of type 'llvm::BinaryOperator'
+  //   00 00 00 00  e0 f7 c5 09 00 00 00 00  20 00 00 00
+  //                ^~~~~~~~~~~
+  //                vptr for 'llvm::BinaryOperator'
+  Die();
+}

Added: compiler-rt/trunk/lib/ubsan/ubsan_handlers_cxx.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/ubsan/ubsan_handlers_cxx.h?rev=166660&view=auto
==============================================================================
--- compiler-rt/trunk/lib/ubsan/ubsan_handlers_cxx.h (added)
+++ compiler-rt/trunk/lib/ubsan/ubsan_handlers_cxx.h Wed Oct 24 21:07:02 2012
@@ -0,0 +1,36 @@
+//===-- ubsan_handlers_cxx.h ------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Entry points to the runtime library for Clang's undefined behavior sanitizer,
+// for C++-specific checks. This code is not linked into C binaries.
+//
+//===----------------------------------------------------------------------===//
+#ifndef UBSAN_HANDLERS_CXX_H
+#define UBSAN_HANDLERS_CXX_H
+
+#include "ubsan_value.h"
+
+namespace __ubsan {
+
+struct DynamicTypeCacheMissData {
+  SourceLocation Loc;
+  const TypeDescriptor &Type;
+  void *TypeInfo;
+  unsigned char TypeCheckKind;
+};
+
+/// \brief Handle a runtime type check failure, caused by an incorrect vptr.
+/// When this handler is called, all we know is that the type was not in the
+/// cache; this does not necessarily imply the existence of a bug.
+extern "C" void __ubsan_handle_dynamic_type_cache_miss(
+  DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash);
+
+}
+
+#endif // UBSAN_HANDLERS_H

Added: compiler-rt/trunk/lib/ubsan/ubsan_type_hash.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/ubsan/ubsan_type_hash.cc?rev=166660&view=auto
==============================================================================
--- compiler-rt/trunk/lib/ubsan/ubsan_type_hash.cc (added)
+++ compiler-rt/trunk/lib/ubsan/ubsan_type_hash.cc Wed Oct 24 21:07:02 2012
@@ -0,0 +1,200 @@
+//===-- ubsan_type_hash.cc ------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementation of a hash table for fast checking of inheritance
+// relationships. This file is only linked into C++ compilations, and is
+// permitted to use language features which require a C++ ABI library.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_type_hash.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+
+// The following are intended to be binary compatible with the definitions
+// given in the Itanium ABI. We make no attempt to be ODR-compatible with
+// those definitions, since existing ABI implementations aren't.
+
+namespace std {
+  class type_info {
+  public:
+    virtual ~type_info();
+  private:
+    const char *__type_name;
+  };
+}
+
+namespace __cxxabiv1 {
+
+/// Type info for classes with no bases, and base class for type info for
+/// classes with bases.
+class __class_type_info : public std::type_info {
+  virtual ~__class_type_info();
+};
+
+/// Type info for classes with simple single public inheritance.
+class __si_class_type_info : public __class_type_info {
+public:
+  virtual ~__si_class_type_info();
+
+  const __class_type_info *__base_type;
+};
+
+class __base_class_type_info {
+public:
+  const __class_type_info *__base_type;
+  long __offset_flags;
+
+  enum __offset_flags_masks {
+    __virtual_mask = 0x1,
+    __public_mask = 0x2,
+    __offset_shift = 8
+  };
+};
+
+/// Type info for classes with multiple, virtual, or non-public inheritance.
+class __vmi_class_type_info : public __class_type_info {
+public:
+  virtual ~__vmi_class_type_info();
+
+  unsigned int flags;
+  unsigned int base_count;
+  __base_class_type_info base_info[1];
+};
+
+}
+
+namespace abi = __cxxabiv1;
+
+// We implement a simple two-level cache for type-checking results. For each
+// (vptr,type) pair, a hash is computed. This hash is assumed to be globally
+// unique; if it collides, we will get false negatives, but:
+//  * such a collision would have to occur on the *first* bad access,
+//  * the probability of such a collision is low (and for a 64-bit target, is
+//    negligible), and
+//  * the vptr, and thus the hash, can be affected by ASLR, so multiple runs
+//    give better coverage.
+//
+// The first caching layer is a small hash table with no chaining; buckets are
+// reused as needed. The second caching layer is a large hash table with open
+// chaining. We can freely evict from either layer since this is just a cache.
+//
+// FIXME: Make these hash table accesses thread-safe. The races here are benign
+//        (worst-case, we could miss a bug or see a slowdown) but we should
+//        avoid upsetting race detectors.
+
+// Find a bucket to store the given value in.
+static __ubsan::HashValue *getTypeCacheHashTableBucket(__ubsan::HashValue V) {
+  static const unsigned HashTableSize = 65537;
+  static __ubsan::HashValue __ubsan_vptr_hash_set[HashTableSize] = { 1 };
+
+  unsigned Probe = V & 65535;
+  for (int Tries = 5; Tries; --Tries) {
+    if (!__ubsan_vptr_hash_set[Probe] || __ubsan_vptr_hash_set[Probe] == V)
+      return &__ubsan_vptr_hash_set[Probe];
+    Probe += ((V >> 16) & 65535) + 1;
+    if (Probe >= HashTableSize)
+      Probe -= HashTableSize;
+  }
+  // FIXME: Pick a random entry from the probe sequence to evict rather than
+  //        just taking the first.
+  return &__ubsan_vptr_hash_set[V];
+}
+
+// A cache of recently-checked hashes. Mini hash table with "random" evictions.
+// The bottom 7 bits of the hash are used as the key.
+static const unsigned CacheSize = 128;
+extern "C" __ubsan::HashValue __ubsan_vptr_type_cache[CacheSize] = { 1 };
+
+/// \brief Determine whether \p Derived has a \p Base base class subobject at
+/// offset \p Offset.
+static bool isDerivedFromAtOffset(const abi::__class_type_info *Derived,
+                                  const abi::__class_type_info *Base,
+                                  sptr Offset) {
+  if (Derived == Base)
+    return Offset == 0;
+
+  if (const abi::__si_class_type_info *SI =
+        dynamic_cast<const abi::__si_class_type_info*>(Derived))
+    return isDerivedFromAtOffset(SI->__base_type, Base, Offset);
+
+  const abi::__vmi_class_type_info *VTI =
+    dynamic_cast<const abi::__vmi_class_type_info*>(Derived);
+  if (!VTI)
+    // No base class subobjects.
+    return false;
+
+  // Look for a zero-offset base class which is derived from \p Base.
+  for (unsigned int base = 0; base != VTI->base_count; ++base) {
+    // FIXME: Curtail the recursion if this base can't possibly contain the
+    //        given offset.
+    sptr OffsetHere = VTI->base_info[base].__offset_flags >>
+                      abi::__base_class_type_info::__offset_shift;
+    if (VTI->base_info[base].__offset_flags &
+          abi::__base_class_type_info::__virtual_mask)
+      // For now, just punt on virtual bases and say 'yes'.
+      // FIXME: OffsetHere is the offset in the vtable of the virtual base
+      //        offset. Read the vbase offset out of the vtable and use it.
+      return true;
+    if (isDerivedFromAtOffset(VTI->base_info[base].__base_type,
+                              Base, Offset - OffsetHere))
+      return true;
+  }
+
+  return false;
+}
+
+namespace {
+
+struct VtablePrefix {
+  /// The offset from the vptr to the start of the most-derived object.
+  /// This should never be greater than zero, and will usually be exactly
+  /// zero.
+  sptr Offset;
+  /// The type_info object describing the most-derived class type.
+  std::type_info *TypeInfo;
+};
+VtablePrefix *getVtablePrefix(void *Object) {
+  VtablePrefix **Ptr = reinterpret_cast<VtablePrefix**>(Object);
+  return *Ptr - 1;
+}
+
+}
+
+bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) {
+  // A crash anywhere within this function probably means the vptr is corrupted.
+  // FIXME: Perform these checks more cautiously.
+
+  // Check whether this is something we've evicted from the cache.
+  HashValue *Bucket = getTypeCacheHashTableBucket(Hash);
+  if (*Bucket == Hash) {
+    __ubsan_vptr_type_cache[Hash % CacheSize] = Hash;
+    return true;
+  }
+
+  VtablePrefix *Vtable = getVtablePrefix(Object);
+  if (Vtable + 1 == 0 || Vtable->Offset > 0)
+    // This can't possibly be a valid vtable.
+    return false;
+
+  // Check that this is actually a type_info object for a class type.
+  abi::__class_type_info *Derived =
+    dynamic_cast<abi::__class_type_info*>(Vtable->TypeInfo);
+  if (!Derived)
+    return false;
+
+  abi::__class_type_info *Base = (abi::__class_type_info*)Type;
+  if (!isDerivedFromAtOffset(Derived, Base, -Vtable->Offset))
+    return false;
+
+  // Success. Cache this result.
+  __ubsan_vptr_type_cache[Hash % CacheSize] = Hash;
+  *Bucket = Hash;
+  return true;
+}

Added: compiler-rt/trunk/lib/ubsan/ubsan_type_hash.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/ubsan/ubsan_type_hash.h?rev=166660&view=auto
==============================================================================
--- compiler-rt/trunk/lib/ubsan/ubsan_type_hash.h (added)
+++ compiler-rt/trunk/lib/ubsan/ubsan_type_hash.h Wed Oct 24 21:07:02 2012
@@ -0,0 +1,29 @@
+//===-- ubsan_type_hash.h ---------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Hashing of types for Clang's undefined behavior checker.
+//
+//===----------------------------------------------------------------------===//
+#ifndef UBSAN_TYPE_HASH_H
+#define UBSAN_TYPE_HASH_H
+
+#include "sanitizer_common/sanitizer_common.h"
+
+namespace __ubsan {
+
+typedef uptr HashValue;
+
+/// \brief Check whether the dynamic type of \p Object has a \p Type subobject
+/// at offset 0.
+/// \return \c true if the type matches, \c false if not.
+bool checkDynamicType(void *Object, void *Type, HashValue CacheSlot);
+
+} // namespace __ubsan
+
+#endif // UBSAN_TYPE_HASH_H





More information about the llvm-commits mailing list