Skip to content
This repository was archived by the owner on May 21, 2019. It is now read-only.

Commit 69470be

Browse files
committed
ubsan: Implement memory permission validation for vtables.
If the pointer passed to the getVtablePrefix function was read from a freed object, we may end up following pointers into objects on the heap and printing bogus dynamic type names in diagnostics. However, we know that vtable pointers will generally only point into memory mapped from object files, not objects on the heap. This change causes us to only follow pointers in a vtable if the vtable and one of the virtual functions it points to appear to have appropriate permissions (i.e. non-writable, and maybe executable), which will generally exclude heap pointers. Only enabled for Linux; this hasn't been tested on FreeBSD, and vtables are writable on Mac (PR24782) so this won't work there. Differential Revision: http://reviews.llvm.org/D12790 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@247484 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 00f3423 commit 69470be

File tree

3 files changed

+75
-0
lines changed

3 files changed

+75
-0
lines changed

lib/ubsan/ubsan_type_hash_itanium.cc

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "ubsan_type_hash.h"
1818

1919
#include "sanitizer_common/sanitizer_common.h"
20+
#include "sanitizer_common/sanitizer_procmaps.h"
2021

2122
// The following are intended to be binary compatible with the definitions
2223
// given in the Itanium ABI. We make no attempt to be ODR-compatible with
@@ -191,7 +192,45 @@ struct VtablePrefix {
191192
/// The type_info object describing the most-derived class type.
192193
std::type_info *TypeInfo;
193194
};
195+
196+
#if SANITIZER_LINUX
197+
bool isValidVptr(void *Vtable) {
198+
// Validate the memory permissions of the vtable pointer and the first
199+
// function pointer in the vtable. They should be r-- or r-x and r-x
200+
// respectively. Only enabled for Linux; this hasn't been tested on FreeBSD,
201+
// and vtables are writable on Mac (PR24782) so this won't work there.
202+
uptr FirstFunctionPtr = *reinterpret_cast<uptr *>(Vtable);
203+
bool ValidVtable = false, ValidFirstFunctionPtr = false;
204+
MemoryMappingLayout Layout(/*cache_enabled=*/true);
205+
uptr Start, End, Prot;
206+
while (Layout.Next(&Start, &End, 0, 0, 0, &Prot)) {
207+
if (Start <= ((uptr)Vtable) && ((uptr)Vtable) <= End &&
208+
(Prot == MemoryMappingLayout::kProtectionRead ||
209+
Prot == (MemoryMappingLayout::kProtectionRead |
210+
MemoryMappingLayout::kProtectionExecute)))
211+
ValidVtable = true;
212+
if (Start <= FirstFunctionPtr && FirstFunctionPtr <= End &&
213+
Prot == (MemoryMappingLayout::kProtectionRead |
214+
MemoryMappingLayout::kProtectionExecute))
215+
ValidFirstFunctionPtr = true;
216+
if (ValidVtable && ValidFirstFunctionPtr)
217+
return true;
218+
}
219+
return false;
220+
}
221+
#else // !SANITIZER_LINUX
222+
bool isValidVptr(void *Vtable) {
223+
return true;
224+
}
225+
#endif
226+
194227
VtablePrefix *getVtablePrefix(void *Vtable) {
228+
if (!IsAccessibleMemoryRange((uptr)Vtable, sizeof(void *)))
229+
return 0;
230+
231+
if (!isValidVptr(Vtable))
232+
return 0;
233+
195234
VtablePrefix *Vptr = reinterpret_cast<VtablePrefix*>(Vtable);
196235
if (!Vptr)
197236
return 0;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// RUN: %clangxx -frtti -fsanitize=vptr -fno-sanitize-recover=vptr -g %s -O3 -o %t
2+
// RUN: not %run %t 2>&1 | FileCheck %s
3+
4+
// Tests that we consider vtable pointers in writable memory to be invalid.
5+
6+
// REQUIRES: vptr-validation
7+
8+
#include <string.h>
9+
10+
struct A {
11+
virtual void f();
12+
};
13+
14+
void A::f() {}
15+
16+
struct B {
17+
virtual void f();
18+
};
19+
20+
void B::f() {}
21+
22+
int main() {
23+
// Create a fake vtable for A in writable memory and copy A's vtable into it.
24+
void *fake_vtable[3];
25+
A a;
26+
void ***vtp = (void ***)&a;
27+
memcpy(fake_vtable, *vtp - 2, sizeof(void *) * 3);
28+
*vtp = fake_vtable + 2;
29+
30+
// A's vtable is invalid because it lives in writable memory.
31+
// CHECK: invalid vptr
32+
reinterpret_cast<B*>(&a)->f();
33+
}

test/ubsan/lit.common.cfg

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,6 @@ if config.host_os == 'Windows':
7777
# because the test hangs or fails on one configuration and not the other.
7878
if config.target_arch.startswith('arm') == False:
7979
config.available_features.add('stable-runtime')
80+
81+
if config.host_os == 'Linux':
82+
config.available_features.add('vptr-validation')

0 commit comments

Comments
 (0)