Summary: | dlopening a load-time library from an earlier library's initializer corrupts TLS state | ||
---|---|---|---|
Product: | glibc | Reporter: | Andy Lutomirski <luto> |
Component: | dynamic-link | Assignee: | Not yet assigned to anyone <unassigned> |
Status: | RESOLVED FIXED | ||
Severity: | normal | CC: | fweimer, sfilargi, tuliom |
Priority: | P2 | Flags: | fweimer:
security-
|
Version: | unspecified | ||
Target Milestone: | 2.22 | ||
Host: | Target: | ||
Build: | Last reconfirmed: |
Description
Andy Lutomirski
2013-02-25 22:13:57 UTC
Here's a self-contained testcase, tested on Fedora 18. --- begin a.c --- #include <dlfcn.h> void a(void) {} extern void abort(void); __attribute__((constructor)) static void init(void) { write(1, "dlopen b\n", 9); if (!dlopen("libb.so", RTLD_LAZY | RTLD_NOLOAD)) /* This corrupts TLS state */ abort(); write(1, "dlopen done\n", 12); } --- end a.c --- --- begin b.c --- static __thread int tls; void b() { write(1, "Begin TLS access\n", 17); tls = 1; /* This will infinite loop because TLS state is corrupt */ write(1, "Done\n", 5); } --- end b.c --- --- begin main.c --- extern void a(void), b(void); int main() { a(); /* Just to DT_NEEDED it. */ b(); /* This one will hang. */ } --- end main.c --- To trigger the bug, do this: $ gcc -g -fPIC -shared -o liba.so a.c $ gcc -g -fPIC -shared -o libb.so b.c $ gcc -g -o main main.c libb.so liba.so -ldl $ LD_LIBRARY_PATH=. ./main dlopen b dlopen done Begin TLS access [this infinite loops] Reversing the link order of libb.so and liba.so will cause this code to work. This issue was fixed in glibc 2.22. Cherry picking the following commit solves the deadlock from this testcase: commit f8aeae347377f3dfa8cbadde057adf1827fb1d44 Author: Alexandre Oliva <aoliva@redhat.com> Date: Tue Mar 17 01:14:11 2015 -0300 Fix DTV race, assert, DTV_SURPLUS Static TLS limit, and nptl_db garbage for ChangeLog [BZ #17090] [BZ #17620] [BZ #17621] [BZ #17628] * NEWS: Update. * elf/dl-tls.c (_dl_update_slotinfo): Clean up outdated DTV entries with Static TLS too. Skip entries past the end of the allocated DTV, from Alan Modra. (tls_get_addr_tail): Update to glibc_likely/unlikely. Move Static TLS DTV entry set up from... (_dl_allocate_tls_init): ... here (fix modid assertion), ... * elf/dl-reloc.c (_dl_nothread_init_static_tls): ... here... * nptl/allocatestack.c (init_one_static_tls): ... and here... * elf/dlopen.c (dl_open_worker): Drop l_tls_modid upper bound for Static TLS. * elf/tlsdeschtab.h (map_generation): Return size_t. Check that the slot we find is associated with the given map before using its generation count. * nptl_db/db_info.c: Include ldsodefs.h. (rtld_global, dtv_slotinfo_list, dtv_slotinfo): New typedefs. * nptl_db/structs.def (DB_RTLD_VARIABLE): New macro. (DB_MAIN_VARIABLE, DB_RTLD_GLOBAL_FIELD): Likewise. (link_map::l_tls_offset): New struct field. (dtv_t::counter): Likewise. (rtld_global): New struct. (_rtld_global): New rtld variable. (dl_tls_dtv_slotinfo_list): New rtld global field. (dtv_slotinfo_list): New struct. (dtv_slotinfo): Likewise. * nptl_db/td_symbol_list.c: Drop gnu/lib-names.h include. (td_lookup): Rename to... (td_mod_lookup): ... this. Use new mod parameter instead of LIBPTHREAD_SO. * nptl_db/td_thr_tlsbase.c: Include link.h. (dtv_slotinfo_list, dtv_slotinfo): New functions. (td_thr_tlsbase): Check DTV generation. Compute Static TLS addresses even if the DTV is out of date or missing them. * nptl_db/fetch-value.c (_td_locate_field): Do not refuse to index zero-length arrays. * nptl_db/thread_dbP.h: Include gnu/lib-names.h. (td_lookup): Make it a macro implemented in terms of... (td_mod_lookup): ... this declaration. * nptl_db/db-symbols.awk (DB_RTLD_VARIABLE): Override. (DB_MAIN_VARIABLE): Likewise. |