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 @@ -191,7 +192,45 @@ /// The type_info object describing the most-derived class type. std::type_info *TypeInfo; }; + +#if SANITIZER_LINUX +bool isValidVptr(void *Vtable) { + // 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(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 true; + } + return false; +} +#else // !SANITIZER_LINUX +bool isValidVptr(void *Vtable) { + return true; +} +#endif + VtablePrefix *getVtablePrefix(void *Vtable) { + if (!IsAccessibleMemoryRange((uptr)Vtable, sizeof(void *))) + return 0; + + if (!isValidVptr(Vtable)) + return 0; + VtablePrefix *Vptr = reinterpret_cast(Vtable); if (!Vptr) return 0; 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 + +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(&a)->f(); +} 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')