Skip to content

Commit 9be8f8b

Browse files
committed
[sanitizer] Simplify GetTls with dl_iterate_phdr
GetTls is the range of * thread control block and optional TLS_PRE_TCB_SIZE * static TLS blocks plus static TLS surplus On glibc, lsan requires the range to include `pthread::{specific_1stblock,specific}` so that allocations only referenced by `pthread_setspecific` can be scanned. This patch uses `dl_iterate_phdr` to collect TLS ranges. Find the one with `dlpi_tls_modid==1` as one of the initially loaded module, then find consecutive ranges. The boundaries give us addr and size. This allows us to drop the glibc internal `_dl_get_tls_static_info` and `InitTlsSize` entirely. Use the simplified method with non-Android Linux for now, but in theory this can be used with *BSD and potentially other ELF OSes. In the future, we can move `ThreadDescriptorSize` code to lsan (and consider intercepting `pthread_setspecific`) to avoid hacks in generic code. See https://reviews.llvm.org/D93972#2480556 for analysis on GetTls usage across various sanitizers. Differential Revision: https://reviews.llvm.org/D98926
1 parent 9d375a4 commit 9be8f8b

16 files changed

+91
-184
lines changed

compiler-rt/lib/asan/asan_rtl.cpp

+1-4
Original file line numberDiff line numberDiff line change
@@ -490,9 +490,6 @@ static void AsanInitInternal() {
490490
if (flags()->start_deactivated)
491491
AsanDeactivate();
492492

493-
// interceptors
494-
InitTlsSize();
495-
496493
// Create main thread.
497494
AsanThread *main_thread = CreateMainThread();
498495
CHECK_EQ(0, main_thread->tid());
@@ -568,7 +565,7 @@ void UnpoisonStack(uptr bottom, uptr top, const char *type) {
568565
type, top, bottom, top - bottom, top - bottom);
569566
return;
570567
}
571-
PoisonShadow(bottom, top - bottom, 0);
568+
PoisonShadow(bottom, RoundUpTo(top - bottom, SHADOW_GRANULARITY), 0);
572569
}
573570

574571
static void UnpoisonDefaultStack() {

compiler-rt/lib/asan/asan_thread.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ void AsanThread::SetThreadStackAndTls(const InitOptions *options) {
307307
uptr stack_size = 0;
308308
GetThreadStackAndTls(tid() == 0, &stack_bottom_, &stack_size, &tls_begin_,
309309
&tls_size);
310-
stack_top_ = stack_bottom_ + stack_size;
310+
stack_top_ = RoundDownTo(stack_bottom_ + stack_size, SHADOW_GRANULARITY);
311311
tls_end_ = tls_begin_ + tls_size;
312312
dtls_ = DTLS_Get();
313313

compiler-rt/lib/hwasan/hwasan.cpp

-2
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,6 @@ void __hwasan_init() {
265265
hwasan_init_is_running = 1;
266266
SanitizerToolName = "HWAddressSanitizer";
267267

268-
InitTlsSize();
269-
270268
CacheBinaryName();
271269
InitializeFlags();
272270

compiler-rt/lib/lsan/lsan.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ extern "C" void __lsan_init() {
9898
InitCommonLsan();
9999
InitializeAllocator();
100100
ReplaceSystemMalloc();
101-
InitTlsSize();
102101
InitializeInterceptors();
103102
InitializeThreadRegistry();
104103
InstallDeadlySignalHandlers(LsanOnDeadlySignal);

compiler-rt/lib/memprof/memprof_rtl.cpp

-3
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,6 @@ static void MemprofInitInternal() {
214214

215215
InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);
216216

217-
// interceptors
218-
InitTlsSize();
219-
220217
// Create main thread.
221218
MemprofThread *main_thread = CreateMainThread();
222219
CHECK_EQ(0, main_thread->tid());

compiler-rt/lib/msan/msan.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,6 @@ void __msan_init() {
436436

437437
InitializeInterceptors();
438438
CheckASLR();
439-
InitTlsSize();
440439
InstallDeadlySignalHandlers(MsanOnDeadlySignal);
441440
InstallAtExitHandler(); // Needs __cxa_atexit interceptor.
442441

compiler-rt/lib/sanitizer_common/sanitizer_common.h

-1
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,6 @@ void SetSandboxingCallback(void (*f)());
284284

285285
void InitializeCoverage(bool enabled, const char *coverage_dir);
286286

287-
void InitTlsSize();
288287
uptr GetTlsSize();
289288

290289
// Other

compiler-rt/lib/sanitizer_common/sanitizer_fuchsia.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,6 @@ void DisableCoreDumperIfNecessary() {}
103103
void InstallDeadlySignalHandlers(SignalHandlerType handler) {}
104104
void SetAlternateSignalStack() {}
105105
void UnsetAlternateSignalStack() {}
106-
void InitTlsSize() {}
107106

108107
bool SignalContext::IsStackOverflow() const { return false; }
109108
void SignalContext::DumpAllRegisters(void *context) { UNIMPLEMENTED(); }

compiler-rt/lib/sanitizer_common/sanitizer_linux.h

-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ class ThreadLister {
9898
// Exposed for testing.
9999
uptr ThreadDescriptorSize();
100100
uptr ThreadSelf();
101-
uptr ThreadSelfOffset();
102101

103102
// Matches a library's file name against a base name (stripping path and version
104103
// information).

compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp

+88-143
Original file line numberDiff line numberDiff line change
@@ -184,80 +184,8 @@ __attribute__((unused)) static bool GetLibcVersion(int *major, int *minor,
184184
#endif
185185
}
186186

187-
#if SANITIZER_GLIBC && !SANITIZER_GO
188-
static uptr g_tls_size;
189-
190-
#ifdef __i386__
191-
#define CHECK_GET_TLS_STATIC_INFO_VERSION (!__GLIBC_PREREQ(2, 27))
192-
#else
193-
#define CHECK_GET_TLS_STATIC_INFO_VERSION 0
194-
#endif
195-
196-
#if CHECK_GET_TLS_STATIC_INFO_VERSION
197-
#define DL_INTERNAL_FUNCTION __attribute__((regparm(3), stdcall))
198-
#else
199-
#define DL_INTERNAL_FUNCTION
200-
#endif
201-
202-
namespace {
203-
struct GetTlsStaticInfoCall {
204-
typedef void (*get_tls_func)(size_t*, size_t*);
205-
};
206-
struct GetTlsStaticInfoRegparmCall {
207-
typedef void (*get_tls_func)(size_t*, size_t*) DL_INTERNAL_FUNCTION;
208-
};
209-
210-
template <typename T>
211-
void CallGetTls(void* ptr, size_t* size, size_t* align) {
212-
typename T::get_tls_func get_tls;
213-
CHECK_EQ(sizeof(get_tls), sizeof(ptr));
214-
internal_memcpy(&get_tls, &ptr, sizeof(ptr));
215-
CHECK_NE(get_tls, 0);
216-
get_tls(size, align);
217-
}
218-
219-
bool CmpLibcVersion(int major, int minor, int patch) {
220-
int ma;
221-
int mi;
222-
int pa;
223-
if (!GetLibcVersion(&ma, &mi, &pa))
224-
return false;
225-
if (ma > major)
226-
return true;
227-
if (ma < major)
228-
return false;
229-
if (mi > minor)
230-
return true;
231-
if (mi < minor)
232-
return false;
233-
return pa >= patch;
234-
}
235-
236-
} // namespace
237-
238-
void InitTlsSize() {
239-
// all current supported platforms have 16 bytes stack alignment
240-
const size_t kStackAlign = 16;
241-
void *get_tls_static_info_ptr = dlsym(RTLD_NEXT, "_dl_get_tls_static_info");
242-
size_t tls_size = 0;
243-
size_t tls_align = 0;
244-
// On i?86, _dl_get_tls_static_info used to be internal_function, i.e.
245-
// __attribute__((regparm(3), stdcall)) before glibc 2.27 and is normal
246-
// function in 2.27 and later.
247-
if (CHECK_GET_TLS_STATIC_INFO_VERSION && !CmpLibcVersion(2, 27, 0))
248-
CallGetTls<GetTlsStaticInfoRegparmCall>(get_tls_static_info_ptr,
249-
&tls_size, &tls_align);
250-
else
251-
CallGetTls<GetTlsStaticInfoCall>(get_tls_static_info_ptr,
252-
&tls_size, &tls_align);
253-
if (tls_align < kStackAlign)
254-
tls_align = kStackAlign;
255-
g_tls_size = RoundUpTo(tls_size, tls_align);
256-
}
257-
#else
258-
void InitTlsSize() { }
259-
#endif // SANITIZER_GLIBC && !SANITIZER_GO
260-
187+
// ThreadDescriptorSize() is only used by lsan to get the pointer to
188+
// thread-specific data keys in the thread control block.
261189
#if (defined(__x86_64__) || defined(__i386__) || defined(__mips__) || \
262190
defined(__aarch64__) || defined(__powerpc64__) || defined(__s390__) || \
263191
defined(__arm__) || SANITIZER_RISCV64) && \
@@ -330,13 +258,6 @@ uptr ThreadDescriptorSize() {
330258
return val;
331259
}
332260

333-
// The offset at which pointer to self is located in the thread descriptor.
334-
const uptr kThreadSelfOffset = FIRST_32_SECOND_64(8, 16);
335-
336-
uptr ThreadSelfOffset() {
337-
return kThreadSelfOffset;
338-
}
339-
340261
#if defined(__mips__) || defined(__powerpc64__) || SANITIZER_RISCV64
341262
// TlsPreTcbSize includes size of struct pthread_descr and size of tcb
342263
// head structure. It lies before the static tls blocks.
@@ -355,48 +276,61 @@ static uptr TlsPreTcbSize() {
355276
}
356277
#endif
357278

358-
uptr ThreadSelf() {
359-
uptr descr_addr;
360-
#if defined(__i386__)
361-
asm("mov %%gs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset));
362-
#elif defined(__x86_64__)
363-
asm("mov %%fs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset));
364-
#elif defined(__mips__)
365-
// MIPS uses TLS variant I. The thread pointer (in hardware register $29)
366-
// points to the end of the TCB + 0x7000. The pthread_descr structure is
367-
// immediately in front of the TCB. TlsPreTcbSize() includes the size of the
368-
// TCB and the size of pthread_descr.
369-
const uptr kTlsTcbOffset = 0x7000;
370-
uptr thread_pointer;
371-
asm volatile(".set push;\
372-
.set mips64r2;\
373-
rdhwr %0,$29;\
374-
.set pop" : "=r" (thread_pointer));
375-
descr_addr = thread_pointer - kTlsTcbOffset - TlsPreTcbSize();
376-
#elif defined(__aarch64__) || defined(__arm__)
377-
descr_addr = reinterpret_cast<uptr>(__builtin_thread_pointer()) -
378-
ThreadDescriptorSize();
379-
#elif SANITIZER_RISCV64
380-
// https://github.com/riscv/riscv-elf-psabi-doc/issues/53
381-
uptr thread_pointer = reinterpret_cast<uptr>(__builtin_thread_pointer());
382-
descr_addr = thread_pointer - TlsPreTcbSize();
383-
#elif defined(__s390__)
384-
descr_addr = reinterpret_cast<uptr>(__builtin_thread_pointer());
385-
#elif defined(__powerpc64__)
386-
// PPC64LE uses TLS variant I. The thread pointer (in GPR 13)
387-
// points to the end of the TCB + 0x7000. The pthread_descr structure is
388-
// immediately in front of the TCB. TlsPreTcbSize() includes the size of the
389-
// TCB and the size of pthread_descr.
390-
const uptr kTlsTcbOffset = 0x7000;
391-
uptr thread_pointer;
392-
asm("addi %0,13,%1" : "=r"(thread_pointer) : "I"(-kTlsTcbOffset));
393-
descr_addr = thread_pointer - TlsPreTcbSize();
394-
#else
395-
#error "unsupported CPU arch"
396-
#endif
397-
return descr_addr;
279+
#if !SANITIZER_GO
280+
namespace {
281+
struct TlsRange {
282+
uptr begin, end, align;
283+
size_t tls_modid;
284+
bool operator<(const TlsRange &rhs) const { return begin < rhs.begin; }
285+
};
286+
} // namespace
287+
288+
static int CollectStaticTlsRanges(struct dl_phdr_info *info, size_t size,
289+
void *data) {
290+
if (!info->dlpi_tls_data)
291+
return 0;
292+
const uptr begin = (uptr)info->dlpi_tls_data;
293+
for (unsigned i = 0; i != info->dlpi_phnum; ++i)
294+
if (info->dlpi_phdr[i].p_type == PT_TLS) {
295+
static_cast<InternalMmapVector<TlsRange> *>(data)->push_back(
296+
TlsRange{begin, begin + info->dlpi_phdr[i].p_memsz,
297+
info->dlpi_phdr[i].p_align, info->dlpi_tls_modid});
298+
break;
299+
}
300+
return 0;
398301
}
399-
#endif // (x86_64 || i386 || MIPS) && SANITIZER_LINUX
302+
303+
static void GetStaticTlsRange(uptr *addr, uptr *size) {
304+
InternalMmapVector<TlsRange> ranges;
305+
dl_iterate_phdr(CollectStaticTlsRanges, &ranges);
306+
uptr len = ranges.size();
307+
Sort(ranges.begin(), len);
308+
// Find the range with tls_modid=1. For glibc, because libc.so uses PT_TLS,
309+
// this module is guaranteed to exist and is one of the initially loaded
310+
// modules.
311+
uptr one = 0;
312+
while (one != len && ranges[one].tls_modid != 1) ++one;
313+
if (one == len) {
314+
// This may happen with musl if no module uses PT_TLS.
315+
*addr = 0;
316+
*size = 0;
317+
return;
318+
}
319+
// Find the maximum consecutive ranges. We consider two modules consecutive if
320+
// the gap is smaller than the alignment. The dynamic loader places static TLS
321+
// blocks this way not to waste space.
322+
uptr l = one;
323+
while (l != 0 && ranges[l].begin < ranges[l - 1].end + ranges[l - 1].align)
324+
--l;
325+
uptr r = one + 1;
326+
while (r != len && ranges[r].begin < ranges[r - 1].end + ranges[r - 1].align)
327+
++r;
328+
*addr = ranges[l].begin;
329+
*size = ranges[r - 1].end - ranges[l].begin;
330+
}
331+
#endif // !SANITIZER_GO
332+
#endif // (x86_64 || i386 || mips || ...) && SANITIZER_LINUX &&
333+
// !SANITIZER_ANDROID
400334

401335
#if SANITIZER_FREEBSD
402336
static void **ThreadSelfSegbase() {
@@ -468,18 +402,36 @@ static void GetTls(uptr *addr, uptr *size) {
468402
*size = 0;
469403
}
470404
#elif SANITIZER_LINUX
405+
GetStaticTlsRange(addr, size);
471406
#if defined(__x86_64__) || defined(__i386__) || defined(__s390__)
472-
*addr = ThreadSelf();
473-
*size = GetTlsSize();
474-
*addr -= *size;
475-
*addr += ThreadDescriptorSize();
476-
#elif defined(__mips__) || defined(__aarch64__) || defined(__powerpc64__) || \
477-
defined(__arm__) || SANITIZER_RISCV64
478-
*addr = ThreadSelf();
479-
*size = GetTlsSize();
407+
// lsan requires the range to additionally cover the static TLS surplus
408+
// (elf/dl-tls.c defines 1664). Otherwise there may be false positives for
409+
// allocations only referenced by tls in dynamically loaded modules.
410+
if (SANITIZER_GLIBC) {
411+
*addr -= 1664;
412+
*size += 1664;
413+
}
414+
// Extend the range to include the thread control block. On glibc, lsan needs
415+
// the range to include pthread::{specific_1stblock,specific} so that
416+
// allocations only referenced by pthread_setspecific can be scanned. This may
417+
// underestimate by at most TLS_TCB_ALIGN-1 bytes but it should be fine
418+
// because the number of bytes after pthread::specific is larger.
419+
*size += ThreadDescriptorSize();
480420
#else
481-
*addr = 0;
482-
*size = 0;
421+
if (SANITIZER_GLIBC)
422+
*size += 1664;
423+
#if defined(__mips__) || defined(__powerpc64__) || SANITIZER_RISCV64
424+
const uptr pre_tcb_size = TlsPreTcbSize();
425+
*addr -= pre_tcb_size;
426+
*size += pre_tcb_size;
427+
#else
428+
// arm and aarch64 reserve two words at TP, so this underestimates the range.
429+
// However, this is sufficient for the purpose of finding the pointers to
430+
// thread-specific data keys.
431+
const uptr tcb_size = ThreadDescriptorSize();
432+
*addr -= tcb_size;
433+
*size += tcb_size;
434+
#endif
483435
#endif
484436
#elif SANITIZER_FREEBSD
485437
void** segbase = ThreadSelfSegbase();
@@ -520,17 +472,11 @@ static void GetTls(uptr *addr, uptr *size) {
520472

521473
#if !SANITIZER_GO
522474
uptr GetTlsSize() {
523-
#if SANITIZER_FREEBSD || SANITIZER_ANDROID || SANITIZER_NETBSD || \
475+
#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
524476
SANITIZER_SOLARIS
525477
uptr addr, size;
526478
GetTls(&addr, &size);
527479
return size;
528-
#elif SANITIZER_GLIBC
529-
#if defined(__mips__) || defined(__powerpc64__) || SANITIZER_RISCV64
530-
return RoundUpTo(g_tls_size + TlsPreTcbSize(), 16);
531-
#else
532-
return g_tls_size;
533-
#endif
534480
#else
535481
return 0;
536482
#endif
@@ -553,10 +499,9 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
553499
if (!main) {
554500
// If stack and tls intersect, make them non-intersecting.
555501
if (*tls_addr > *stk_addr && *tls_addr < *stk_addr + *stk_size) {
556-
CHECK_GT(*tls_addr + *tls_size, *stk_addr);
557-
CHECK_LE(*tls_addr + *tls_size, *stk_addr + *stk_size);
558-
*stk_size -= *tls_size;
559-
*tls_addr = *stk_addr + *stk_size;
502+
if (*stk_addr + *stk_size < *tls_addr + *tls_size)
503+
*tls_size = *stk_addr + *stk_size - *tls_addr;
504+
*stk_size = *tls_addr - *stk_addr;
560505
}
561506
}
562507
#endif

compiler-rt/lib/sanitizer_common/sanitizer_mac.cpp

-3
Original file line numberDiff line numberDiff line change
@@ -548,9 +548,6 @@ uptr GetTlsSize() {
548548
return 0;
549549
}
550550

551-
void InitTlsSize() {
552-
}
553-
554551
uptr TlsBaseAddr() {
555552
uptr segbase = 0;
556553
#if defined(__x86_64__)

compiler-rt/lib/sanitizer_common/sanitizer_rtems.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,6 @@ void DisableCoreDumperIfNecessary() {}
106106
void InstallDeadlySignalHandlers(SignalHandlerType handler) {}
107107
void SetAlternateSignalStack() {}
108108
void UnsetAlternateSignalStack() {}
109-
void InitTlsSize() {}
110109

111110
void SignalContext::DumpAllRegisters(void *context) {}
112111
const char *DescribeSignalOrException(int signo) { UNIMPLEMENTED(); }

compiler-rt/lib/sanitizer_common/sanitizer_win.cpp

-3
Original file line numberDiff line numberDiff line change
@@ -846,9 +846,6 @@ uptr GetTlsSize() {
846846
return 0;
847847
}
848848

849-
void InitTlsSize() {
850-
}
851-
852849
void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
853850
uptr *tls_addr, uptr *tls_size) {
854851
#if SANITIZER_GO

0 commit comments

Comments
 (0)