This is an archive of the discontinued LLVM Phabricator instance.

Correct handling of the TLS/NetBSD block of the main program
ClosedPublic

Authored by krytarowski on Nov 16 2017, 4:41 PM.

Details

Summary

Include <sys/tls.h> for:

  • struct tls_tcb - thread control block structure
  • HAVE_LWP_GETTCB_FAST - __lwp_gettcb_fast() is available
  • HAVE_LWP_GETPRIVATE_FAST - __lwp_getprivate_fast() is available
  • __HAVE_TLS_VARIANT_I - TLS Variant I for this architecture
  • __HAVE_TLS_VARIANT_II - TLS Variant II for this architecture

Rename ThreadSelfSegbase() to ThreadSelfTlsTcb and switch it
to retrieve in a portable way TCB.

Switch ThreadSelf() to retrieve pthread from struct tcb_tls.

Use dl_iterate_phdr() to find out the size of TLS block of
the main program.

Correct the index of the TLS block of the main program
(dlpi_tls_modid); it's 1, not 2.

New NetBSD code is now CPU (NetBSD port) agnostic.

Stop sharing the same code with FreeBSD.

Sponsored by <The NetBSD Foundation>

Diff Detail

Repository
rL LLVM

Event Timeline

krytarowski created this revision.Nov 16 2017, 4:41 PM
dvyukov edited edge metadata.Nov 17 2017, 12:02 AM

Joerg, please review.

joerg added inline comments.Nov 17 2017, 7:10 AM
lib/sanitizer_common/sanitizer_linux_libcdep.cc
347 ↗(On Diff #123270)

The fallback is just using _lwp_getprivate() directly. No need to error out.

404 ↗(On Diff #123270)

What is this code trying to do in first place? It doesn't really make sense to me. Handling variant I and II differently doesn't make sense either.

krytarowski added inline comments.Nov 17 2017, 12:35 PM
lib/sanitizer_common/sanitizer_linux_libcdep.cc
347 ↗(On Diff #123270)

I wanted to stop rotting and don't fallback to else when there is no included <sys/tls.h>.. however it would fail anyway. I will adjust it. I think I will also rename ThreadSelfSegbase() to ThreadSelfTLS()

404 ↗(On Diff #123270)

Retrieve allocated TLS vector, address and size. How to handle it differently? How to add Variant I support? Is there a way to use a generic solution for both Variations?

It looks like the proper TLS vector should be in a form of std::vector<std::pair<uptr,uptr>>, as there might be multiple regions mapped (true for DSO).

The problem is that there is no public interface to retrieve this and with introspecting exported symbols in /usr/libexec/ld.elf_so this is untrivial.

If there are more users of this use-case (beyond sanitizers) it's a legitimate reason to develop a dedicated API.

It seems like Linux uses __libc_memalign() interceptor to catch dynamically allocated TLS blocks.

a thread's static TLS area can be found using the internal glibc function _dl_get_tls_static_info() (like in TSan). On x86 and x86-64 this area includes the thread descriptor, which contains pointers to thread-specific data. On the other hand, dynamically allocated TLS blocks can't always be reached easily through the thread descriptor. We handle them by intercepting the __libc_memalign calls that the linker uses to allocate dynamic TLS space, and considering all blocks allocated from the linker to be live.

joerg edited edge metadata.Nov 19 2017, 7:50 AM

The public interface for obtaining the TLS storage is the combination of reading the DTV vector of a thread in combination with dl_iterate_phdr to find the size of the TLS block of a specific module. That gives you all that you need to know. It is important to keep in mind that the vector can be initialized lazily, so __tls_get_addr and friends will have to be intercepted to update the global view.

krytarowski planned changes to this revision.Nov 19 2017, 8:41 AM

I will be back to it once I will fix other bugs, unrelated to TLS in TSan.

Back to it as required for MSan.

krytarowski edited the summary of this revision. (Show Details)
  • Fix bug with static TLS vector index.
  • Reuse dl_iterate_phdr().
  • Simplify code.
krytarowski retitled this revision from Make TLS/NetBSD handling more generic to Make static TLS/NetBSD handling more generic.EditedDec 5 2017, 5:37 AM

@joerg done. I will improve handling of dynamically allocated vectors in another revision.

joerg added inline comments.Dec 5 2017, 7:44 AM
lib/sanitizer_common/sanitizer_linux_libcdep.cc
421 ↗(On Diff #125503)

The index 1 is not used for the static vector, but the TLS block of the main program. It will be unset of the main program has no TLS segment.

krytarowski retitled this revision from Make static TLS/NetBSD handling more generic to Correct handling of the TLS/NetBSD block of the main program.
krytarowski edited the summary of this revision. (Show Details)
krytarowski marked 5 inline comments as done.

I've addressed the comments in review.

As discussed with Joerg, this TLS handling is questionable by design, but the proposed NetBSD code is not worse than any other OS.

I'm going to commit it as it is now (without click 'accepted').

vitalybuka accepted this revision.Dec 8 2017, 6:19 PM
vitalybuka added inline comments.
lib/sanitizer_common/sanitizer_linux_libcdep.cc
49 ↗(On Diff #125532)

please combine two #if SANITIZER_NETBSD blocks

This revision is now accepted and ready to land.Dec 8 2017, 6:19 PM
This revision was automatically updated to reflect the committed changes.
This revision was automatically updated to reflect the committed changes.
MaskRay added a subscriber: MaskRay.Jan 1 2021, 8:28 PM

@krytarowski I think this patch may be incorrect.

On TLS Variant I (i386, x86-64), static TLS blocks ... tp tcb

Notable users of GetTls:

  • asan calls ClearShadowForThreadStackAndTLS to mark the TLS block addressable.
  • lsan calls ScanRangeForPointers on the TLS block to find reachable chunks.
  • msan and tsan's __tls_get_addr interceptors unpoison/reset dtv ranges to avoid false positives
  • A glibc workaround (D93866): I suspect it is no longer needed, but I'll confirm with glibc dev.

The size should be the total size of static TLS blocks (including the sizeof of TCB, (struct pthread on musl), (including tcbhead_t on glibc))), not just p_memsz of the executable PT_TLS.

The FreeBSD definition is quite correct, though it does not include the sizeof of TCB. For the users of GetTls, not including the sizeof of TCB probably does not matter (writes of the TCB members are not instrumented by asan/lsan/msan/tsan).