Summary: | Running libc.so.6 as an executable triggers IFUNC relocation ordering issues | ||
---|---|---|---|
Product: | glibc | Reporter: | Matej Krizan <krizan> |
Component: | dynamic-link | Assignee: | Florian Weimer <fweimer> |
Status: | RESOLVED FIXED | ||
Severity: | normal | CC: | carlos, fweimer, krizan, marat, shanzhikun, siddhesh, tuliom |
Priority: | P3 | Flags: | fweimer:
security-
|
Version: | 2.24 | ||
Target Milestone: | 2.33 | ||
See Also: |
https://sourceware.org/bugzilla/show_bug.cgi?id=21041 https://bugzilla.redhat.com/show_bug.cgi?id=1428369 https://sourceware.org/bugzilla/show_bug.cgi?id=26615 |
||
Host: | Target: | ||
Build: | Last reconfirmed: | 2020-12-11 00:00:00 | |
Attachments: |
libc output
gdb output uname output 64bit preload library |
Created attachment 9701 [details]
gdb output
Created attachment 9702 [details]
uname output
Created attachment 9704 [details] 64bit preload library From the 4.0.82.0 package https://download.eset.com/com/eset/apps/home/eav/linux/latest/eset_nod32av_64bit_en.linux Dependency sorting always puts the main executable first, under the assumption that nothing can have a DT_NEEDED dependency on it. When libc.so.6 is executed as an executable, it takes that position, rather than its usual position in the dependency order. Relocation of objects precedes in a depth-first order. This means that libpthread.so.0 is relocated before the main executable (libc.so.6 in this case). Relocation processing for the __gettimeofday symbol reference in libpthread.so.0 calls the IFUNC resolver for this symbol in libc.so.6. However, libc.so.6 has not been relocated at this point, and the reference to _rtld_global_ro (behind the GLRO macro) is undefined. So in short, this is another IFUNC ordering issue. The good news is that this particular one can only happen if libc.so.6 is the main executable. Otherwise, libc.so.6 is relocated before libpthread.so.0. Shorter reproducer: LD_PRELOAD=/lib64/libpthread.so.0 /lib64/libc.so.6 (In reply to Florian Weimer from comment #4) > Dependency sorting always puts the main executable first, under the > assumption that nothing can have a DT_NEEDED dependency on it. > > When libc.so.6 is executed as an executable, it takes that position, rather > than its usual position in the dependency order. > > Relocation of objects precedes in a depth-first order. This means that > libpthread.so.0 is relocated before the main executable (libc.so.6 in this > case). > > Relocation processing for the __gettimeofday symbol reference in > libpthread.so.0 calls the IFUNC resolver for this symbol in libc.so.6. > > However, libc.so.6 has not been relocated at this point, and the reference > to _rtld_global_ro (behind the GLRO macro) is undefined. > > So in short, this is another IFUNC ordering issue. The good news is that > this particular one can only happen if libc.so.6 is the main executable. > Otherwise, libc.so.6 is relocated before libpthread.so.0. > > Shorter reproducer: > > LD_PRELOAD=/lib64/libpthread.so.0 /lib64/libc.so.6 The libesets_pac.so is preloaded and needs to have all of it's dependencies initialized first. I think it's a dynamic loader bug that all the DT_NEEDED of the preloaded DSO are not initialized first. Though I agree this is a corner case, but one which we could fix with proper dependency handling. Ok, thank you very much for the investigation. Patches posted: https://sourceware.org/pipermail/libc-alpha/2020-December/120635.html https://sourceware.org/pipermail/libc-alpha/2020-December/120636.html The master branch has been updated by Florian Weimer <fw@sourceware.org>: https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=9ffa50b26b0cb5d3043adf6d3d0b1ea735acc147 commit 9ffa50b26b0cb5d3043adf6d3d0b1ea735acc147 Author: Florian Weimer <fweimer@redhat.com> Date: Fri Dec 11 17:30:03 2020 +0100 elf: Include libc.so.6 as main program in dependency sort (bug 20972) _dl_map_object_deps always sorts the initially loaded object first during dependency sorting. This means it is relocated last in dl_open_worker. This results in crashes in IFUNC resolvers without lazy bindings if libraries are preloaded that refer to IFUNCs in libc.so.6: the resolvers are called when libc.so.6 has not been relocated yet, so references to _rtld_global_ro etc. crash. The fix is to check against the libc.so.6 link map recorded by the __libc_early_init framework, and let it participate in the dependency sort. This fixes bug 20972. Reviewed-by: Carlos O'Donell <carlos@redhat.com> Fixed for glibc 2.33. Thanks, Florian. A follow-up fix is needed: https://sourceware.org/pipermail/libc-alpha/2020-December/120720.html Hi, Florian, Will this fix be synchronized to glibc 2.28? I think version 2.28 also has this problem. (In reply to Sdrkun from comment #12) > Hi, Florian, Will this fix be synchronized to glibc 2.28? I think version > 2.28 also has this problem. I have backported this into 2.28, but changes are rather invasive. The second fix (also in 2.33): commit 4d0985543f479a6f421d4d8a9e0d1dc71c9c2c53 Author: Florian Weimer <fweimer@redhat.com> Date: Tue Dec 15 20:56:04 2020 +0100 elf: Record libc.so link map when it is the main program (bug 20972) Otherwise, it will not participate in the dependency sorting. Fixes commit 9ffa50b26b0cb5d3043adf6d3d0b1ea735acc147 ("elf: Include libc.so.6 as main program in dependency sort (bug 20972)"). Reviewed-by: Carlos O'Donell <carlos@redhat.com> |
Created attachment 9700 [details] libc output I have a problem with latest glibc 2.24 and our on-access preload library on 64bit Fedora 25 (the same also with glibc 2.17 on CentOS 7 and on 32bit archs). It seems, that the dynamic linker is crashing with SIGSEGV while preloading libesets_pac.so when libc.so is started as a normal program: LD_DEBUG=all LD_PRELOAD=./libesets_pac.so /lib64/libc-2.24.so > libc.txt 2>&1 I got a populated backtrace only when attaching gdb to the shell and following forked child: #0 0x00005623257aa717 in _dl_vdso_vsym ( name=name@entry=0x5623257efce1 "__vdso_gettimeofday", vers=vers@entry=0x7ffe43447760) at ../sysdeps/unix/sysv/linux/dl-vdso.c:27 #1 0x0000562325720ec7 in __gettimeofday () at ../sysdeps/unix/sysv/linux/x86/gettimeofday.c:40 #2 0x00007fdb915f8f50 in elf_machine_rela (skip_ifunc=<optimized out>, reloc_addr_arg=<optimized out>, version=<optimized out>, sym=<optimized out>, reloc=0x7fdb913d2de8, map=0x7fdb9180fbb0) at ../sysdeps/x86_64/dl-machine.h:314 #3 elf_dynamic_do_Rela (skip_ifunc=<optimized out>, lazy=<optimized out>, nrelative=<optimized out>, relsize=<optimized out>, reladdr=<optimized out>, map=0x7fdb9180fbb0) at do-rel.h:137 #4 _dl_relocate_object (scope=<optimized out>, reloc_mode=<optimized out>, consider_profiling=<optimized out>, consider_profiling@entry=0) at dl-reloc.c:259 #5 0x00007fdb915f0051 in dl_main (phdr=<optimized out>, phnum=<optimized out>, user_entry=<optimized out>, auxv=<optimized out>) at rtld.c:2066 #6 0x00007fdb9160591f in _dl_sysdep_start ( start_argptr=start_argptr@entry=0x7ffe43447b20, dl_main=dl_main@entry=0x7fdb915ed9a0 <dl_main>) at ../elf/dl-sysdep.c:249 #7 0x00007fdb915f0f68 in _dl_start_final (arg=0x7ffe43447b20) at rtld.c:305 #8 _dl_start (arg=0x7ffe43447b20) at rtld.c:411 #9 0x00007fdb915eccd8 in _start () from /lib64/ld-linux-x86-64.so.2 #10 0x0000000000000001 in ?? () #11 0x00007ffe434493a3 in ?? () #12 0x0000000000000000 in ?? () However, no crash when running any other dynamically linked program (you have to create an empty file /opt/eset/esets/sbin/esets_daemon first), e.g.: LD_DEBUG=all LD_PRELOAD=./libesets_pac.so /bin/uname -r > uname.txt 2>&1 Full details and outputs attached. Our preload library is built on Debian 4.0 for compatibility reasons. Does someone know what's going on there?