Bug 20972

Summary: Running libc.so.6 as an executable triggers IFUNC relocation ordering issues
Product: glibc Reporter: Matej Krizan <krizan>
Component: dynamic-linkAssignee: 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

Description Matej Krizan 2016-12-15 14:35:22 UTC
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?
Comment 1 Matej Krizan 2016-12-15 14:36:06 UTC
Created attachment 9701 [details]
gdb output
Comment 2 Matej Krizan 2016-12-15 14:36:31 UTC
Created attachment 9702 [details]
uname output
Comment 3 Matej Krizan 2016-12-15 18:26:58 UTC
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
Comment 4 Florian Weimer 2017-01-26 16:35:08 UTC
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
Comment 5 Carlos O'Donell 2017-01-26 16:57:48 UTC
(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.
Comment 6 Matej Krizan 2017-02-08 07:16:21 UTC
Ok, thank you very much for the investigation.
Comment 8 Sourceware Commits 2020-12-11 17:31:55 UTC
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>
Comment 9 Florian Weimer 2020-12-11 17:33:35 UTC
Fixed for glibc 2.33.
Comment 10 Matej Krizan 2020-12-14 06:47:02 UTC
Thanks, Florian.
Comment 11 Florian Weimer 2020-12-15 11:03:11 UTC
A follow-up fix is needed: https://sourceware.org/pipermail/libc-alpha/2020-December/120720.html
Comment 12 Sdrkun 2021-03-04 14:40:57 UTC
Hi, Florian, Will this fix be synchronized to glibc 2.28? I think version 2.28 also has this problem.
Comment 13 Florian Weimer 2021-03-05 18:31:47 UTC
(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.
Comment 14 Florian Weimer 2021-03-05 18:32:23 UTC
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>