This is the mail archive of the
glibc-bugs@sourceware.org
mailing list for the glibc project.
[Bug dynamic-link/22787] New: _dl_check_caller returns false when libc is linked through an absolute DT_NEEDED path
- From: "ento.entotto at gmail dot com" <sourceware-bugzilla at sourceware dot org>
- To: glibc-bugs at sourceware dot org
- Date: Mon, 05 Feb 2018 02:51:17 +0000
- Subject: [Bug dynamic-link/22787] New: _dl_check_caller returns false when libc is linked through an absolute DT_NEEDED path
- Auto-submitted: auto-generated
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.