This is an archive of the discontinued LLVM Phabricator instance.

[sanitizer] Fix runtime crash on 32-bit Linux with glibc 2.27

Authored by Lekensteyn on May 9 2018, 7:49 AM.



The calling convention for _dl_get_tls_static_info symbol has changed on
32-bit systems since glibc 2.27. Make sure to support older and newer
32-bit glibc versions with the same runtime library.

Reuse the version check from InitTlsSize. Use gnu_get_libc_version
rather than confstr(_CS_GNU_LIBC_VERSION) because the latter is
intercepted by msan. This symbol existed since February 1998.

This patch does not try to address other issues with glibc 2.25.
Tested with 32/64-bit glibc 2.26/2.27 on Arch Linux.


This is an alternative patch to D44623 by Jakub, addressing some review comments from there (reduce ifdef magic, coding style) and using a different approach for version detection. If desired, GetGlibVersion runtime could be guarded under a __GLIBC_PREREQ check, but there is potentially much more code that could be hidden that way so I did not do this.

Test: all check-sanitizers tests pass on Linux with glibc 2.27 except SanitizerCommon-asan-i386-Linux (unrelated) and SanitizerCommon-lsan-x86_64-Linux (not sure, probably also unrelated).

Diff Detail

Event Timeline

Lekensteyn created this revision.May 9 2018, 7:49 AM
jakubjelinek added inline comments.May 9 2018, 8:04 AM

So you both call dlsym (instead of the function directly) and then parse the string? That is significantly larger and more expensive than just calling the dlvsym. Furthermore, other code in the sanitizers use confstr for this purpose.


Ugh, so you call this unconditionally on all architectures, just because one needs it?

Lekensteyn added inline comments.May 9 2018, 8:28 AM

Detecting glibc 2.27 by lookup up the glob symbol seems a bit indirect, that is why I preferred an approach that directly checks the version.

There is only one confstr(_CS_GNU_LIBC_VERSION) user (which got removed below). String parsing costs should be negligible and there are enough dlsym calls for interceptors such that this additional one should not matter.

I think that the few cycles extra here is worth the lower code maintenance cost that would otherwise be necessary for indirectly retrieving strconf without breaking msan. Correct me if I am wrong :)


What about this?

int minor = 0, patch = 0;
if (CHECK_GET_TLS_STATIC_INFO_VERSION) GetGlibcVersion(&minor, &patch);
if (CHECK_GET_TLS_STATIC_INFO && minor && minor < 27) {

Alternative: make GetGlibcVersion return (minor << 8) | patch or a large number if parsing failed for some reason. Then: if (CHECK_GET_TLS_STATIC_INFO && GetGlibcVersion() < 27).

Lekensteyn abandoned this revision.May 9 2018, 10:17 AM

D44623 looks better now, the version check of this patch has some more issues than worth solving.

vitalybuka added inline comments.May 9 2018, 11:17 AM

I'd split this into two patches:

  1. Extract code into GetGlibcVersion
  2. GetTls changes

I am not sure that we need these
CHECK_EQ(sizeof(get_tls), sizeof(get_tls_static_info_ptr))
If not we can go with:

static bool IsGetTlsInternal() {

return false;

int minor;
GetGlibcVersion(&minor, &patch);
return minor && minor < 27;

static void GetTlsSize(size_t *tls_size, size_t *tls_align) {

void *get_tls_static_info_ptr = dlsym(RTLD_NEXT, "_dl_get_tls_static_info");
CHECK_NE(get_tls_static_info_ptr, 0);
if (IsGetTlsInternal()) {
  typedef void (*get_tls_func)(size_t *, size_t *);
  ((get_tls_func)get_tls_static_info_ptr)(tls_size, tls_align);
} else {
  typedef void (*get_tls_func)(size_t *, size_t *) DL_INTERNAL_FUNCTION;
  ((get_tls_func)get_tls_static_info_ptr)(tls_size, tls_align);