This is the mail archive of the glibc-bugs@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Bug dynamic-link/22787] New: _dl_check_caller returns false when libc is linked through an absolute DT_NEEDED path


https://sourceware.org/bugzilla/show_bug.cgi?id=22787

            Bug ID: 22787
           Summary: _dl_check_caller returns false when libc is linked
                    through an absolute DT_NEEDED path
           Product: glibc
           Version: 2.23
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: dynamic-link
          Assignee: unassigned at sourceware dot org
          Reporter: ento.entotto at gmail dot com
  Target Milestone: ---

## Steps to reproduce

- Clone this repo: https://github.com/ento/glibc-absolute-path-link-mcve
- Install patchelf https://github.com/NixOS/patchelf
- Run build.sh, optionally with a prefix of a custom glibc installation
(assumes interpreter is named `ld-linux-x86-64.so.2`). 
- This created an executable ./bad that pulls in libc.so.6 through an absolute
path:

```
$ readelf -d bad

Dynamic section at offset 0x1ab0 contains 21 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library:
[/usr/local/glibc-2.25/lib/libc.so.6]
```


- Expected: executing ./bad prints out the result of looking up the IP address
of www.bbc.com
- Actual: executing ./bad either results in segmentation fault, or the error
message "Name or service not known," depending on the version of glibc.

Note: this segmentation fault is an example of
https://sourceware.org/bugzilla/show_bug.cgi?id=16368

## Workaround

Enabling profiling of the dynamic linker does away with the error:

```
$ LD_PROFILE=a ./bad
151.101.52.81
151.101.52.81
151.101.52.81
```

## Discussion

dl_open_worker calls _dl_check_caller to check if it has been called by
internal code. _dl_check_caller takes the address of the call site, finds the
shared library where the address is located in, and checks if the l_name and
l_libname of the library is one of the library SONAMEs permitted:
https://sourceware.org/git/?p=glibc.git;a=blob;f=elf/dl-caller.c;h=81a77af4eb12fe57b3fef28fdbc7f16d398a740c;hb=65f6c94e682e3c21bde4c3aea74011c00d41ac04

When libc.so.6 is loaded through an absolute path, both l_name and the only
entry in the l_libname list have that absolute path as the value.

``` 
(gdb) print _rtld_global._dl_ns[0]._ns_loaded->l_next->l_next->l_name
$5 = 0x6f0000226d40
"/nix/store/z0b60y0khix9jb74ka56gw7b7n9s8awx-glibc-2.26-131/lib/libc.so.6"
(gdb) print _rtld_global._dl_ns[0]._ns_loaded->l_next->l_next->l_libname->name
$6 = 0x79e81ff44490
"/nix/store/z0b60y0khix9jb74ka56gw7b7n9s8awx-glibc-2.26-131/lib/libc.so.6"
(gdb) print _rtld_global._dl_ns[0]._ns_loaded->l_next->l_next->l_libname->next
$7 = (struct libname_list *) 0x0
```

Because _dl_check_caller matches the permitted SONAMEs against those absolute
paths, the check fails even if the caller is libc itself.

I haven't looked at the exact conditions by which a library's SONAME is added
to its l_libname, but it seems to be done lazily. (search for
add_name_to_object in dl-load.c:
https://sourceware.org/git/?p=glibc.git;a=blob;f=elf/dl-load.c;h=7554a99b5acb153762ab8967f330309ba9c69821;hb=65f6c94e682e3c21bde4c3aea74011c00d41ac04#l1878

Enabling LD_PROFILE functions as a workaround because it forces
dl_map_object_from_fd to always add the SONAME of a library that's been loaded
to its l_libname:
https://sourceware.org/git/?p=glibc.git;a=blob;f=elf/dl-load.c;h=7554a99b5acb153762ab8967f330309ba9c69821;hb=65f6c94e682e3c21bde4c3aea74011c00d41ac04#l1310

When libc is linked through its filename, l_libname is set to the filename
itself, which happens to match the SONAME, making the check pass. (My
understanding is a little shaky here.)

```
$ gdb -iex "set debug-file-directory /nix/debug-symbols-0bki/lib/debug" ./good
(gdb) break _dl_check_caller
(gdb) run
(gdb) print _rtld_global._dl_ns[0]._ns_loaded->l_next->l_next->l_name
$2 = 0x6f0000226d40
"/nix/store/z0b60y0khix9jb74ka56gw7b7n9s8awx-glibc-2.26-131/lib/libc.so.6"
(gdb) print _rtld_global._dl_ns[0]._ns_loaded->l_next->l_next->l_libname->name
$3 = 0x7ffff6ff8490 "libc.so.6"
(gdb) print _rtld_global._dl_ns[0]._ns_loaded->l_next->l_next->l_libname->next
$4 = (struct libname_list *) 0x0
```

Note: Here's a stacktrace at the point of a segmentation fault, if that's
useful:

```
#0  _dl_debug_initialize (ldbase=ldbase@entry=0, ns=-2) at dl-debug.c:58
#1  0x00006f00000132fb in _dl_open (file=0x7ffe494db960 "libnss_files.so.2",
mode=<optimized out>, 
    caller_dlopen=0x79e81fca3730 <nss_load_library+240>, nsid=<optimized out>,
argc=1, argv=<optimized out>, env=0x7ffe494dc4e8)
    at dl-open.c:677
#2  0x000079e81fcba13d in do_dlopen (ptr=ptr@entry=0x7ffe494db930) at
dl-libc.c:87
#3  0x000079e81fcbaa91 in __GI__dl_catch_error
(objname=objname@entry=0x7ffe494db910,
errstring=errstring@entry=0x7ffe494db918, 
    mallocedp=mallocedp@entry=0x7ffe494db90f,
operate=operate@entry=0x79e81fcba100 <do_dlopen>,
args=args@entry=0x7ffe494db930)
    at dl-error-skeleton.c:198
#4  0x000079e81fcba1c7 in dlerror_run (operate=operate@entry=0x79e81fcba100
<do_dlopen>, args=args@entry=0x7ffe494db930)
    at dl-libc.c:46
#5  0x000079e81fcba241 in __GI___libc_dlopen_mode
(name=name@entry=0x7ffe494db960 "libnss_files.so.2",
mode=mode@entry=-2147483647)
    at dl-libc.c:163
#6  0x000079e81fca3730 in nss_load_library (ni=ni@entry=0x79e81ff47660) at
nsswitch.c:358
#7  0x000079e81fca3f28 in __GI___nss_lookup_function (ni=0x79e81ff47660,
fct_name=fct_name@entry=0x79e81fd0504c "gethostbyname2_r")
    at nsswitch.c:455
#8  0x000079e81fca401c in __GI___nss_lookup (ni=ni@entry=0x7ffe494dba98, 
    fct_name=fct_name@entry=0x79e81fd0504c "gethostbyname2_r",
fct2_name=fct2_name@entry=0x0, fctp=fctp@entry=0x7ffe494dba90)
    at nsswitch.c:189
#9  0x000079e81fca5190 in __GI___nss_hosts_lookup2 (ni=ni@entry=0x7ffe494dba98, 
    fct_name=fct_name@entry=0x79e81fd0504c "gethostbyname2_r",
fct2_name=fct2_name@entry=0x0, fctp=fctp@entry=0x7ffe494dba90)
    at XXX-lookup.c:75
#10 0x000079e81fc94407 in __gethostbyname2_r (name=name@entry=0x40098c
"www.bbc.com", af=af@entry=2, 
    resbuf=resbuf@entry=0x7ffe494dbc70, buffer=0x7ffe494dbea0 "\220\006@",
buflen=1024, result=result@entry=0x7ffe494dbc68, 
    h_errnop=0x79e81ff45524) at ../nss/getXXbyYY_r.c:270
#11 0x000079e81fc6d5c8 in gaih_inet (name=name@entry=0x40098c "www.bbc.com",
service=<optimized out>, 
    req=req@entry=0x600d40 <hints>, pai=pai@entry=0x7ffe494dbe28,
naddrs=naddrs@entry=0x7ffe494dbe24, 
    tmpbuf=tmpbuf@entry=0x7ffe494dbe90) at ../sysdeps/posix/getaddrinfo.c:595
#12 0x000079e81fc6e4b5 in __GI_getaddrinfo (name=<optimized out>,
service=<optimized out>, hints=0x600d40 <hints>, 
    pai=0x600d70 <infoptr>) at ../sysdeps/posix/getaddrinfo.c:2304
#13 0x0000000000400804 in main () at getaddrinfo.c:13
```

## Possible fixes

1. Linking through an absolute path is not supported; wontfix
2. Add SONAME to l_libname when it matches one of the SONAMEs checked by
_dl_check_caller
3. Set the basename of the DT_NEEDED path as the first l_libname 

I'm new to the codebase and don't have a strong opinion nor intuition on what
should be done with this issue.

## Context

I'm trying out nix (the package manager) on a Chromebook, which sets
LD_LIBRARY_PATH=/usr/local/lib64 by default in /etc/profile.env.

This messes up with nix's ELF files, which rely on RUNPATH to load its shared
libraries; system-wide libraries end up taking precedence over nix's which are
located under /nix/store.

I thought maybe [rewriting all DT_NEEDED values as absolute
paths](https://github.com/NixOS/nixpkgs/issues/24844) would resolve this issue,
and wrote a script that does it, but then ran into this error.

## Environment

1. Chrome OS Version 63.0.3239.140 (Official Build) (64-bit), gcc 4.9.4
2. Ubuntu 16.04, gcc 5.4.0 

glibc versions checked:

- 2.23 (stock Chrome OS, stock Xenial)
- 2.25 (compiled on Chrome OS)
- 2.26 (pulled from nix on Chrome OS)
- HEAD as of Jan 26 compiled on Chrome OS
(65f6c94e682e3c21bde4c3aea74011c00d41ac04)

-- 
You are receiving this mail because:
You are on the CC list for the bug.

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]