This is an archive of the discontinued LLVM Phabricator instance.

[sanitizer] Introduce a vDSO aware time function, and use it in the allocator
AbandonedPublic

Authored by cryptoad on Nov 30 2017, 9:40 AM.

Details

Summary

NanoTime is a time sink when checking whether or not to release memory to
the OS. While reducing the amount of calls to said function is in the works,
another solution that was found to be beneficial was to use a timing function
that can leverage the vDSO.

We hit a couple of snags along the way, like the fact that the glibc crashes
when clock_gettime is called from a preinit_array, or the fact that
__vdso_clock_gettime is mangled (for security purposes) and can't be used
directly, and also that clock_gettime can be intercepted.

The proposed solution takes care of all this as far as I can tell, and
significantly improve performances and some Scudo load tests with memory
reclaiming enabled.

@krytarowski: could you please chime in on the NetBSD aspect of it?

Event Timeline

cryptoad created this revision.Nov 30 2017, 9:40 AM
alekseyshl added inline comments.Nov 30 2017, 11:41 AM
lib/sanitizer_common/sanitizer_linux.cc
466

It feels fragile to depend on the implementation, but I don't really see what else can we do. Have a test to check that it works from preinit_array on Android?

470

What about __kernel_clock_gettime?

krytarowski edited edge metadata.Nov 30 2017, 11:46 AM

NetBSD does not ship with vDSO and the code should/could be disabled for it.

mcgrathr edited edge metadata.Nov 30 2017, 11:47 AM

Fuchsia bit LGTM. Linux bit highly suspect.

lib/sanitizer_common/sanitizer_linux.cc
468

Don't rely on these libc internals. Just look up the real __vdso_clock_gettime symbol in the vDSO itself. On some machines glibc defines a __vdso_clock_gettime symbol also, which is a mangled pointer, not a function as declared here. But that's purely a libc implementation detail (it's a GLIBC_PRIVATE symbol) and you shouldn't pay attention to that at all. On x86, there is no such symbol in libc and so __vdso_clock_gettime will reach the actual vDSO function. But since the name is sometimes overloaded, what you probably want to do is find the vDSO with dlopen or suchlike and then use dlsym to look up __vdso_clock_gettime directly in the vDSO.

Additionally NetBSD requires symbol mangling: clock_gettime -> __clock_gettime50.

cryptoad added inline comments.Nov 30 2017, 11:54 AM
lib/sanitizer_common/sanitizer_linux.cc
466

I have test/scudo/preinit.c that I could enable again on Android (it crashes on N for another reason due to a Bionic bug).
Otherwise ASan has a few tests that leverage the preinit_array, I am going to check if they actually end up calling the function.

470

So it turns out that __kernel_clock_gettime@version is the vDSO symbol name.
The glibc looks it up, and stores it in __vdso_clock_gettime (without appendix, glibc symbol, not vDSO): https://github.com/lattera/glibc/blob/a2f34833b1042d5d8eeb263b4cf4caaea138c4ad/sysdeps/unix/sysv/linux/s390/init-first.c#L42 (example for s390).
What we really want is to make sure that the glibc symbol isn't null.

@krytarowski: so directly call the syscall on NetBSD or is there a possible performance gain calling the libc function as opposed to the syscall?

lib/sanitizer_common/sanitizer_linux.cc
468

So __vdso_clock_gettime here is not used in any capacity but very that if it exists, it's not null.
The reason being that calling clock_gettime from a preinit_array crashes on a NULL deref (or bad demangled pointer deref).
In an ideal world I don't think that should be the case, so I am trying to work around this.

While I totally agree that it's hackish, it felt more portable than going through our own vDSO symbol resolution (with multiple architectures, multiple symbold names, etc), which it's already done at some point by the libc.
Regarding alternatives:

  • dlsym uses calloc and I am not really keen on adding a special calloc case from dlsym as ASan does;
  • doing our own ELF parsing could be the alternative and getting the base of the vDSO via getauxval, but the ratio of additional code & complexity versus value seems low.

@krytarowski: so directly call the syscall on NetBSD or is there a possible performance gain calling the libc function as opposed to the syscall?

Just call the syscall for NetBSD. "clock_gettime" under mangled name "__clock_gettime50".

cryptoad updated this revision to Diff 124994.Nov 30 2017, 12:53 PM

Updating BSD support.

cryptoad abandoned this revision.Nov 30 2017, 1:49 PM

I am going to redo this because it's actually libc specific, so for Linux it should be in linuc_libcdep, but then a flurry of other issues arise.