diff options
author | Peter Wu <peter@lekensteyn.nl> | 2018-06-10 11:17:47 +0000 |
---|---|---|
committer | Peter Wu <peter@lekensteyn.nl> | 2018-06-10 11:17:47 +0000 |
commit | 722b6f3f67b805ca27f01db149c32ffebb90b398 (patch) | |
tree | 1054782baf1b1cf8932ba2ea93b7b9d47f661918 | |
parent | edbb9725b1a9a6e98bd652ad60f132d88a135ca5 (diff) |
[ASAN] Fix crash on i?86-linux (32-bit) against glibc 2.27 and laterlinaro-local/D47948
Summary:
Running sanitized 32-bit x86 programs on glibc 2.27 crashes at startup, with:
ERROR: AddressSanitizer: SEGV on unknown address 0xf7a8a250 (pc 0xf7f807f4 bp 0xff969fc8 sp 0xff969f7c T16777215)
The signal is caused by a WRITE memory access.
#0 0xf7f807f3 in _dl_get_tls_static_info (/lib/ld-linux.so.2+0x127f3)
#1 0xf7a92599 (/lib/libasan.so.5+0x112599)
#2 0xf7a80737 (/lib/libasan.so.5+0x100737)
#3 0xf7f7e14f in _dl_init (/lib/ld-linux.so.2+0x1014f)
#4 0xf7f6eb49 (/lib/ld-linux.so.2+0xb49)
The problem is that glibc changed the calling convention for the GLIBC_PRIVATE
symbol that sanitizer uses (even when it should not, GLIBC_PRIVATE is exactly
for symbols that can change at any time, be removed etc.), see
https://sourceware.org/ml/libc-alpha/2017-08/msg00497.html
Fixes https://github.com/google/sanitizers/issues/954
Patch By: Jakub Jelinek
Reviewed By: vitalybuka, Lekensteyn
Differential Revison: https://reviews.llvm.org/D44623
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@334363 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | lib/sanitizer_common/sanitizer_linux_libcdep.cc | 45 |
1 files changed, 38 insertions, 7 deletions
diff --git a/lib/sanitizer_common/sanitizer_linux_libcdep.cc b/lib/sanitizer_common/sanitizer_linux_libcdep.cc index c08b6054b..20840ce7f 100644 --- a/lib/sanitizer_common/sanitizer_linux_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_linux_libcdep.cc @@ -162,24 +162,55 @@ bool SetEnv(const char *name, const char *value) { static uptr g_tls_size; #ifdef __i386__ +# ifndef __GLIBC_PREREQ +# define CHECK_GET_TLS_STATIC_INFO_VERSION 1 +# else +# define CHECK_GET_TLS_STATIC_INFO_VERSION (!__GLIBC_PREREQ(2, 27)) +# endif +#else +# define CHECK_GET_TLS_STATIC_INFO_VERSION 0 +#endif + +#if CHECK_GET_TLS_STATIC_INFO_VERSION # define DL_INTERNAL_FUNCTION __attribute__((regparm(3), stdcall)) #else # define DL_INTERNAL_FUNCTION #endif +namespace { +struct GetTlsStaticInfoCall { + typedef void (*get_tls_func)(size_t*, size_t*); +}; +struct GetTlsStaticInfoRegparmCall { + typedef void (*get_tls_func)(size_t*, size_t*) DL_INTERNAL_FUNCTION; +}; + +template <typename T> +void CallGetTls(void* ptr, size_t* size, size_t* align) { + typename T::get_tls_func get_tls; + CHECK_EQ(sizeof(get_tls), sizeof(ptr)); + internal_memcpy(&get_tls, &ptr, sizeof(ptr)); + CHECK_NE(get_tls, 0); + get_tls(size, align); +} +} // namespace + void InitTlsSize() { // all current supported platforms have 16 bytes stack alignment const size_t kStackAlign = 16; - typedef void (*get_tls_func)(size_t*, size_t*) DL_INTERNAL_FUNCTION; - get_tls_func get_tls; void *get_tls_static_info_ptr = dlsym(RTLD_NEXT, "_dl_get_tls_static_info"); - CHECK_EQ(sizeof(get_tls), sizeof(get_tls_static_info_ptr)); - internal_memcpy(&get_tls, &get_tls_static_info_ptr, - sizeof(get_tls_static_info_ptr)); - CHECK_NE(get_tls, 0); size_t tls_size = 0; size_t tls_align = 0; - get_tls(&tls_size, &tls_align); + // On i?86, _dl_get_tls_static_info used to be internal_function, i.e. + // __attribute__((regparm(3), stdcall)) before glibc 2.27 and is normal + // function in 2.27 and later. + if (CHECK_GET_TLS_STATIC_INFO_VERSION && + !dlvsym(RTLD_NEXT, "glob", "GLIBC_2.27")) + CallGetTls<GetTlsStaticInfoRegparmCall>(get_tls_static_info_ptr, + &tls_size, &tls_align); + else + CallGetTls<GetTlsStaticInfoCall>(get_tls_static_info_ptr, + &tls_size, &tls_align); if (tls_align < kStackAlign) tls_align = kStackAlign; g_tls_size = RoundUpTo(tls_size, tls_align); |