[PATCH] D12790: ubsan: Implement memory permission validation for vtables.

Peter Collingbourne via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 10 19:00:16 PDT 2015


pcc updated this revision to Diff 34523.
pcc added a comment.

- Address comments


http://reviews.llvm.org/D12790

Files:
  lib/ubsan/ubsan_type_hash_itanium.cc
  test/ubsan/TestCases/TypeCheck/vptr-bad-perms.cpp
  test/ubsan/lit.common.cfg

Index: test/ubsan/lit.common.cfg
===================================================================
--- test/ubsan/lit.common.cfg
+++ test/ubsan/lit.common.cfg
@@ -77,3 +77,6 @@
 # because the test hangs or fails on one configuration and not the other.
 if config.target_arch.startswith('arm') == False:
   config.available_features.add('stable-runtime')
+
+if config.host_os == 'Linux':
+  config.available_features.add('vptr-validation')
Index: test/ubsan/TestCases/TypeCheck/vptr-bad-perms.cpp
===================================================================
--- /dev/null
+++ test/ubsan/TestCases/TypeCheck/vptr-bad-perms.cpp
@@ -0,0 +1,33 @@
+// RUN: %clangxx -frtti -fsanitize=vptr -fno-sanitize-recover=vptr -g %s -O3 -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+
+// Tests that we consider vtable pointers in writable memory to be invalid.
+
+// REQUIRES: vptr-validation
+
+#include <string.h>
+
+struct A {
+  virtual void f();
+};
+
+void A::f() {}
+
+struct B {
+  virtual void f();
+};
+
+void B::f() {}
+
+int main() {
+  // Create a fake vtable for A in writable memory and copy A's vtable into it.
+  void *fake_vtable[3];
+  A a;
+  void ***vtp = (void ***)&a;
+  memcpy(fake_vtable, *vtp - 2, sizeof(void *) * 3);
+  *vtp = fake_vtable + 2;
+
+  // A's vtable is invalid because it lives in writable memory.
+  // CHECK: invalid vptr
+  reinterpret_cast<B*>(&a)->f();
+}
Index: lib/ubsan/ubsan_type_hash_itanium.cc
===================================================================
--- lib/ubsan/ubsan_type_hash_itanium.cc
+++ lib/ubsan/ubsan_type_hash_itanium.cc
@@ -17,6 +17,7 @@
 #include "ubsan_type_hash.h"
 
 #include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_procmaps.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
@@ -192,6 +193,33 @@
   std::type_info *TypeInfo;
 };
 VtablePrefix *getVtablePrefix(void *Vtable) {
+  if (!IsAccessibleMemoryRange((uptr)Vtable, sizeof(void *)))
+    return 0;
+
+#if SANITIZER_LINUX
+  // Validate the memory permissions of the vtable pointer and the first
+  // function pointer in the vtable. They should be r-- or r-x and r-x
+  // respectively. Only enabled for Linux; this hasn't been tested on FreeBSD,
+  // and vtables are writable on Mac (PR24782) so this won't work there.
+  uptr FirstFunctionPtr = *reinterpret_cast<uptr *>(Vtable);
+  bool ValidVtable = false, ValidFirstFunctionPtr = false;
+  MemoryMappingLayout Layout(/*cache_enabled=*/true);
+  uptr Start, End, Prot;
+  while (Layout.Next(&Start, &End, 0, 0, 0, &Prot)) {
+    if (Start <= ((uptr)Vtable) && ((uptr)Vtable) <= End &&
+        (Prot == MemoryMappingLayout::kProtectionRead ||
+         Prot == (MemoryMappingLayout::kProtectionRead |
+                  MemoryMappingLayout::kProtectionExecute)))
+      ValidVtable = true;
+    if (Start <= FirstFunctionPtr && FirstFunctionPtr <= End &&
+        Prot == (MemoryMappingLayout::kProtectionRead |
+                 MemoryMappingLayout::kProtectionExecute))
+      ValidFirstFunctionPtr = true;
+  }
+  if (!ValidVtable || !ValidFirstFunctionPtr)
+    return 0;
+#endif
+
   VtablePrefix *Vptr = reinterpret_cast<VtablePrefix*>(Vtable);
   if (!Vptr)
     return 0;


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D12790.34523.patch
Type: text/x-patch
Size: 3307 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20150911/1448a5b4/attachment.bin>


More information about the llvm-commits mailing list