1 //===-- sanitizer_tls_get_addr.cpp ----------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Handle the __tls_get_addr call. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "sanitizer_tls_get_addr.h" 14 15 #include "sanitizer_allocator_interface.h" 16 #include "sanitizer_atomic.h" 17 #include "sanitizer_flags.h" 18 #include "sanitizer_platform_interceptors.h" 19 20 namespace __sanitizer { 21 #if SANITIZER_INTERCEPT_TLS_GET_ADDR 22 23 // The actual parameter that comes to __tls_get_addr 24 // is a pointer to a struct with two words in it: 25 struct TlsGetAddrParam { 26 uptr dso_id; 27 uptr offset; 28 }; 29 30 // This must be static TLS 31 __attribute__((tls_model("initial-exec"))) 32 static __thread DTLS dtls; 33 34 // Make sure we properly destroy the DTLS objects: 35 // this counter should never get too large. 36 static atomic_uintptr_t number_of_live_dtls; 37 38 static const uptr kDestroyedThread = -1; 39 40 static void DTLS_Deallocate(DTLS::DTVBlock *block) { 41 VReport(2, "__tls_get_addr: DTLS_Deallocate %p\n", (void *)block); 42 UnmapOrDie(block, sizeof(DTLS::DTVBlock)); 43 atomic_fetch_sub(&number_of_live_dtls, 1, memory_order_relaxed); 44 } 45 46 static DTLS::DTVBlock *DTLS_NextBlock(atomic_uintptr_t *cur) { 47 uptr v = atomic_load(cur, memory_order_acquire); 48 if (v == kDestroyedThread) 49 return nullptr; 50 DTLS::DTVBlock *next = (DTLS::DTVBlock *)v; 51 if (next) 52 return next; 53 DTLS::DTVBlock *new_dtv = 54 (DTLS::DTVBlock *)MmapOrDie(sizeof(DTLS::DTVBlock), "DTLS_NextBlock"); 55 uptr prev = 0; 56 if (!atomic_compare_exchange_strong(cur, &prev, (uptr)new_dtv, 57 memory_order_seq_cst)) { 58 UnmapOrDie(new_dtv, sizeof(DTLS::DTVBlock)); 59 return (DTLS::DTVBlock *)prev; 60 } 61 uptr num_live_dtls = 62 atomic_fetch_add(&number_of_live_dtls, 1, memory_order_relaxed); 63 VReport(2, "__tls_get_addr: DTLS_NextBlock %p %zd\n", (void *)&dtls, 64 num_live_dtls); 65 return new_dtv; 66 } 67 68 static DTLS::DTV *DTLS_Find(uptr id) { 69 VReport(2, "__tls_get_addr: DTLS_Find %p %zd\n", (void *)&dtls, id); 70 static constexpr uptr kPerBlock = ARRAY_SIZE(DTLS::DTVBlock::dtvs); 71 DTLS::DTVBlock *cur = DTLS_NextBlock(&dtls.dtv_block); 72 if (!cur) 73 return nullptr; 74 for (; id >= kPerBlock; id -= kPerBlock) cur = DTLS_NextBlock(&cur->next); 75 return cur->dtvs + id; 76 } 77 78 void DTLS_Destroy() { 79 if (!common_flags()->intercept_tls_get_addr) return; 80 VReport(2, "__tls_get_addr: DTLS_Destroy %p\n", (void *)&dtls); 81 DTLS::DTVBlock *block = (DTLS::DTVBlock *)atomic_exchange( 82 &dtls.dtv_block, kDestroyedThread, memory_order_release); 83 while (block) { 84 DTLS::DTVBlock *next = 85 (DTLS::DTVBlock *)atomic_load(&block->next, memory_order_acquire); 86 DTLS_Deallocate(block); 87 block = next; 88 } 89 } 90 91 #if defined(__powerpc64__) || defined(__mips__) 92 // This is glibc's TLS_DTV_OFFSET: 93 // "Dynamic thread vector pointers point 0x8000 past the start of each 94 // TLS block." (sysdeps/<arch>/dl-tls.h) 95 static const uptr kDtvOffset = 0x8000; 96 #elif defined(__riscv) 97 // This is glibc's TLS_DTV_OFFSET: 98 // "Dynamic thread vector pointers point 0x800 past the start of each 99 // TLS block." (sysdeps/riscv/dl-tls.h) 100 static const uptr kDtvOffset = 0x800; 101 #else 102 static const uptr kDtvOffset = 0; 103 #endif 104 105 extern "C" { 106 SANITIZER_WEAK_ATTRIBUTE 107 uptr __sanitizer_get_allocated_size(const void *p); 108 109 SANITIZER_WEAK_ATTRIBUTE 110 const void *__sanitizer_get_allocated_begin(const void *p); 111 } 112 113 DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res, 114 uptr static_tls_begin, uptr static_tls_end) { 115 if (!common_flags()->intercept_tls_get_addr) return 0; 116 TlsGetAddrParam *arg = reinterpret_cast<TlsGetAddrParam *>(arg_void); 117 uptr dso_id = arg->dso_id; 118 DTLS::DTV *dtv = DTLS_Find(dso_id); 119 if (!dtv || dtv->beg) 120 return 0; 121 uptr tls_size = 0; 122 uptr tls_beg = reinterpret_cast<uptr>(res) - arg->offset - kDtvOffset; 123 VReport(2, 124 "__tls_get_addr: %p {0x%zx,0x%zx} => %p; tls_beg: 0x%zx; sp: %p " 125 "num_live_dtls %zd\n", 126 (void *)arg, arg->dso_id, arg->offset, res, tls_beg, (void *)&tls_beg, 127 atomic_load(&number_of_live_dtls, memory_order_relaxed)); 128 if (dtls.last_memalign_ptr == tls_beg) { 129 tls_size = dtls.last_memalign_size; 130 VReport(2, "__tls_get_addr: glibc <=2.24 suspected; tls={0x%zx,0x%zx}\n", 131 tls_beg, tls_size); 132 } else if (tls_beg >= static_tls_begin && tls_beg < static_tls_end) { 133 // This is the static TLS block which was initialized / unpoisoned at thread 134 // creation. 135 VReport(2, "__tls_get_addr: static tls: 0x%zx\n", tls_beg); 136 tls_size = 0; 137 } else if (const void *start = 138 __sanitizer_get_allocated_begin((void *)tls_beg)) { 139 tls_beg = (uptr)start; 140 tls_size = __sanitizer_get_allocated_size(start); 141 VReport(2, "__tls_get_addr: glibc >=2.25 suspected; tls={0x%zx,0x%zx}\n", 142 tls_beg, tls_size); 143 } else { 144 VReport(2, "__tls_get_addr: Can't guess glibc version\n"); 145 // This may happen inside the DTOR of main thread, so just ignore it. 146 tls_size = 0; 147 } 148 dtv->beg = tls_beg; 149 dtv->size = tls_size; 150 return dtv; 151 } 152 153 void DTLS_on_libc_memalign(void *ptr, uptr size) { 154 if (!common_flags()->intercept_tls_get_addr) return; 155 VReport(2, "DTLS_on_libc_memalign: %p 0x%zx\n", ptr, size); 156 dtls.last_memalign_ptr = reinterpret_cast<uptr>(ptr); 157 dtls.last_memalign_size = size; 158 } 159 160 DTLS *DTLS_Get() { return &dtls; } 161 162 bool DTLSInDestruction(DTLS *dtls) { 163 return atomic_load(&dtls->dtv_block, memory_order_relaxed) == 164 kDestroyedThread; 165 } 166 167 #else 168 void DTLS_on_libc_memalign(void *ptr, uptr size) {} 169 DTLS::DTV *DTLS_on_tls_get_addr(void *arg, void *res, 170 unsigned long, unsigned long) { return 0; } 171 DTLS *DTLS_Get() { return 0; } 172 void DTLS_Destroy() {} 173 bool DTLSInDestruction(DTLS *dtls) { 174 UNREACHABLE("dtls is unsupported on this platform!"); 175 } 176 177 #endif // SANITIZER_INTERCEPT_TLS_GET_ADDR 178 179 } // namespace __sanitizer 180