This is an archive of the discontinued LLVM Phabricator instance.

ubsan: Implement memory permission validation for vtables.
ClosedPublic

Authored by pcc on Sep 10 2015, 6:21 PM.

Details

Summary

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.

Diff Detail

Repository
rL LLVM

Event Timeline

pcc updated this revision to Diff 34520.Sep 10 2015, 6:21 PM
pcc retitled this revision from to ubsan: Implement memory permission validation for vtables..
pcc updated this object.
pcc added reviewers: kcc, krasin, rsmith.
pcc added a subscriber: llvm-commits.
kcc added inline comments.Sep 10 2015, 6:28 PM
lib/ubsan/ubsan_type_hash_itanium.cc
199 ↗(On Diff #34520)

can this be if (SANITIZER_LINUX)?

test/ubsan/TestCases/Linux/TypeCheck/vptr-bad-perms.cpp
2 ↗(On Diff #34520)

add an explanation comment to the test

test/ubsan/TestCases/Linux/lit.local.cfg
1 ↗(On Diff #34520)

I wonder if we can use something like

REQUIRES: linux

instead of putting this test into a separate dir.
We don't have this exact requirement now, but quite a few others.

pcc updated this revision to Diff 34523.Sep 10 2015, 7:00 PM
  • Address comments
lib/ubsan/ubsan_type_hash_itanium.cc
199 ↗(On Diff #34520)

I don't think it can. The code may create references to undefined symbols on (e.g.) Windows.

test/ubsan/TestCases/Linux/TypeCheck/vptr-bad-perms.cpp
2 ↗(On Diff #34520)

Done

test/ubsan/TestCases/Linux/lit.local.cfg
1 ↗(On Diff #34520)

Introduced REQUIRES: vptr-validation.

kcc added inline comments.Sep 11 2015, 10:18 AM
lib/ubsan/ubsan_type_hash_itanium.cc
199 ↗(On Diff #34523)

ifdefs inside the function body is a great evil.

Alternatively, or maybe even much better, I'd ask you to move this functionality into a separate function
somewhere in sanitizer_common.

pcc updated this revision to Diff 34594.Sep 11 2015, 3:13 PM
  • Move vptr validation to a separate function
lib/ubsan/ubsan_type_hash_itanium.cc
199 ↗(On Diff #34523)

This code isn't really general purpose enough to live in sanitizer_common. I've moved it into a separate function in this file.

kcc accepted this revision.Sep 11 2015, 3:15 PM
kcc edited edge metadata.

LGTM

This revision is now accepted and ready to land.Sep 11 2015, 3:15 PM
samsonov accepted this revision.Sep 11 2015, 3:19 PM
samsonov added a reviewer: samsonov.
samsonov added a subscriber: samsonov.

LGTM. Thanks!

lib/ubsan/ubsan_type_hash_itanium.cc
197 ↗(On Diff #34594)

These two functions should be static

This revision was automatically updated to reflect the committed changes.
pcc added inline comments.Sep 11 2015, 3:21 PM
lib/ubsan/ubsan_type_hash_itanium.cc
197 ↗(On Diff #34594)

They are both in an anonymous namespace, so they don't need to be.

sberg added a subscriber: sberg.Sep 21 2015, 7:19 AM

If this gets re-enabled after r248157 "Revert 'ubsan: Implement memory permission validation for vtables,'" please consider modifying isValidVptr as

   MemoryMappingLayout Layout(/*cache_enabled=*/true);
   uptr Start, End, Prot;
   while (Layout.Next(&Start, &End, 0, 0, 0, &Prot)) {
+    Prot &= ~MemoryMappingLayout::kProtectionShared;
     if (Start <= ((uptr)Vtable) && ((uptr)Vtable) <= End &&
         (Prot == MemoryMappingLayout::kProtectionRead ||
          Prot == (MemoryMappingLayout::kProtectionRead |

for code like LibreOffice that synthesizes vtables at runtime, and can do so via the double-mmap technique described at http://www.akkadia.org/drepper/selinux-mem.html (to appease SELinux),