Skip to content

Commit

Permalink
Fix tls_get_addr handling for glibc >=2.25
Browse files Browse the repository at this point in the history
This changes the sanitizers' tls_get_addr handling from
a heuristic check of __signal_safe_memalign allocations
(which has only been used in a since deprecated version
of Google's runtime), to using the sanitizers' interface
function to check if it is a malloc allocation (used
since glibc >= 2.25).

This is one of the approaches proposed by Keno in
google/sanitizers#1409 (comment)

This moves the weak annotation of __sanitizer_get_allocated_size/begin from the header to sanitizer_tls_get_addr.cpp, as suggested by Vitaly in D148060.

Reviewed By: vitalybuka

Differential Revision: https://reviews.llvm.org/D147459
  • Loading branch information
thurstond committed Apr 14, 2023
1 parent c2bba2d commit b1bd52c
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE
uptr __sanitizer_get_estimated_allocated_size(uptr size);
SANITIZER_INTERFACE_ATTRIBUTE int __sanitizer_get_ownership(const void *p);
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE const void *
__sanitizer_get_allocated_begin(const void *p);
SANITIZER_INTERFACE_ATTRIBUTE const void *__sanitizer_get_allocated_begin(
const void *p);
SANITIZER_INTERFACE_ATTRIBUTE uptr
__sanitizer_get_allocated_size(const void *p);
SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_current_allocated_bytes();
Expand Down
29 changes: 15 additions & 14 deletions compiler-rt/lib/sanitizer_common/sanitizer_tls_get_addr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "sanitizer_tls_get_addr.h"

#include "sanitizer_allocator_interface.h"
#include "sanitizer_atomic.h"
#include "sanitizer_flags.h"
#include "sanitizer_platform_interceptors.h"
Expand All @@ -26,13 +27,6 @@ struct TlsGetAddrParam {
uptr offset;
};

// Glibc starting from 2.19 allocates tls using __signal_safe_memalign,
// which has such header.
struct Glibc_2_19_tls_header {
uptr size;
uptr start;
};

// This must be static TLS
__attribute__((tls_model("initial-exec")))
static __thread DTLS dtls;
Expand Down Expand Up @@ -108,6 +102,14 @@ static const uptr kDtvOffset = 0x800;
static const uptr kDtvOffset = 0;
#endif

extern "C" {
SANITIZER_WEAK_ATTRIBUTE
uptr __sanitizer_get_allocated_size(const void *p);

SANITIZER_WEAK_ATTRIBUTE
const void *__sanitizer_get_allocated_begin(const void *p);
}

DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res,
uptr static_tls_begin, uptr static_tls_end) {
if (!common_flags()->intercept_tls_get_addr) return 0;
Expand All @@ -125,19 +127,18 @@ DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res,
atomic_load(&number_of_live_dtls, memory_order_relaxed));
if (dtls.last_memalign_ptr == tls_beg) {
tls_size = dtls.last_memalign_size;
VReport(2, "__tls_get_addr: glibc <=2.18 suspected; tls={0x%zx,0x%zx}\n",
VReport(2, "__tls_get_addr: glibc <=2.24 suspected; tls={0x%zx,0x%zx}\n",
tls_beg, tls_size);
} else if (tls_beg >= static_tls_begin && tls_beg < static_tls_end) {
// This is the static TLS block which was initialized / unpoisoned at thread
// creation.
VReport(2, "__tls_get_addr: static tls: 0x%zx\n", tls_beg);
tls_size = 0;
} else if ((tls_beg % 4096) == sizeof(Glibc_2_19_tls_header)) {
// We may want to check gnu_get_libc_version().
Glibc_2_19_tls_header *header = (Glibc_2_19_tls_header *)tls_beg - 1;
tls_size = header->size;
tls_beg = header->start;
VReport(2, "__tls_get_addr: glibc >=2.19 suspected; tls={0x%zx 0x%zx}\n",
} else if (const void *start =
__sanitizer_get_allocated_begin((void *)tls_beg)) {
tls_beg = (uptr)start;
tls_size = __sanitizer_get_allocated_size(start);
VReport(2, "__tls_get_addr: glibc >=2.25 suspected; tls={0x%zx,0x%zx}\n",
tls_beg, tls_size);
} else {
VReport(2, "__tls_get_addr: Can't guess glibc version\n");
Expand Down
26 changes: 17 additions & 9 deletions compiler-rt/lib/sanitizer_common/sanitizer_tls_get_addr.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,24 @@
// the lack of interface that would tell us about the Dynamic TLS (DTLS).
// https://sourceware.org/bugzilla/show_bug.cgi?id=16291
//
// The matters get worse because the glibc implementation changed between
// 2.18 and 2.19:
// https://groups.google.com/forum/#!topic/address-sanitizer/BfwYD8HMxTM
//
// Before 2.19, every DTLS chunk is allocated with __libc_memalign,
// Before 2.25: every DTLS chunk is allocated with __libc_memalign,
// which we intercept and thus know where is the DTLS.
// Since 2.19, DTLS chunks are allocated with __signal_safe_memalign,
// which is an internal function that wraps a mmap call, neither of which
// we can intercept. Luckily, __signal_safe_memalign has a simple parseable
// header which we can use.
//
// Since 2.25: DTLS chunks are allocated with malloc. We could co-opt
// the malloc interceptor to keep track of the last allocation, similar
// to how we handle __libc_memalign; however, this adds some overhead
// (since malloc, unlike __libc_memalign, is commonly called), and
// requires care to avoid false negatives for LeakSanitizer.
// Instead, we rely on our internal allocators - which keep track of all
// its allocations - to determine if an address points to a malloc
// allocation.
//
// There exists a since-deprecated version of Google's internal glibc fork
// that used __signal_safe_memalign. DTLS_on_tls_get_addr relied on a
// heuristic check (is the allocation 16 bytes from the start of a page
// boundary?), which was sometimes erroneous:
// https://bugs.chromium.org/p/chromium/issues/detail?id=1275223#c15
// Since that check has no practical use anymore, we have removed it.
//
//===----------------------------------------------------------------------===//

Expand Down
4 changes: 0 additions & 4 deletions compiler-rt/test/msan/dtls_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@
// Reports use-of-uninitialized-value, not analyzed
XFAIL: target={{.*netbsd.*}}
// This is known to be broken with glibc-2.27+
// https://bugs.llvm.org/show_bug.cgi?id=37804
XFAIL: glibc-2.27
*/

#ifndef BUILD_SO
Expand Down

0 comments on commit b1bd52c

Please sign in to comment.