Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Bug#1011113: chrpath: misreads, damages shared libraries with unexpected structure

0 views
Skip to first unread message

FeRD (Frank Dana)

unread,
May 17, 2022, 3:10:03 AM5/17/22
to
Package: chrpath
Version: 0.16-2
Severity: important
Tags: patch upstream

Dear Maintainer,

I'm forwarding this analysis and patch from a corresponding Fedora report
(https://bugzilla.redhat.com/show_bug.cgi?id=2022931), as we have the same
unreachable Debian URL listed as upstream
(https://alioth.debian.org/projects/chrpath/).

I first encountered and diagnosed this issue on a Fedora install, but have
since verified chrpath in Ubuntu 20.04 (and by extension, Debian) is affected
by the same bug. The steps to verify (below) are slightly different, but the
outcome is the same.

My patch to fix the bug (submitted to Fedora in
https://src.fedoraproject.org/rpms/chrpath/pull-request/6, but also included
below) should be applicable as well.

The issue arises when chrpath is asked to read or modify an ELF binary where
the .dynstr header section (into which the offset of the RUNPATH is recorded)
is NOT the first header section of type SHT_STRTAB in the headers. When reading
or modifying an ELF binary, chrpath uses the first SHT_STRTAB section it finds
as the symbol table to modify, without verifying that it is the correct .dynstr
table.

Binaries with a .dynstr header section located AFTER other header sections of
the same type will be created when using the patchelf tool to add a RUNPATH to
a library which was originally built without one, though that is likely not the
only possible means of creating such a file.

To reproduce with current chrpath, patchelf, binutils (for readelf), coreutils
(for realpath), libc-bin (for ldd), and gcc packages installed (I used a Ubuntu
20.04 VM with gcc 9):

PREPARATION

1. Create a file library.c with the following contents:

$ cd /var/tmp # (any writable directory will do)
$ cat > library.c
#include <stdio.h>
void test_lib(void) {
printf("%s\n", "Hello, shared library");
}
^D

2. Compile as a shared library 'library2.so':

$ gcc -shared -fPIC -o library2.so library.c

3. Compile a second time as 'library.so':

$ gcc -shared -fPIC -o library.so library.c

4. Make 'library.so' dynamically link to 'library2.so':

$ patchelf --add-needed library2.so library.so
$ ldd library.so
linux-vdso.so.1 (0x00007ffed6781000)
library2.so => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f08fc8b5000)
/lib64/ld-linux-x86-64.so.2 (0x00007f08fcad6000)

5. Use 'patchelf' to add a RUNPATH so that one library can find the other:

$ patchelf --print-rpath library.so

$ patchelf --set-rpath $(realpath .) library.so
$ patchelf --print-rpath library.so
/var/tmp
$ ldd library.so
linux-vdso.so.1 (0x00007ffca37f9000)
library2.so => /var/tmp/library2.so (0x00007f788f39d000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f788f184000)
/lib64/ld-linux-x86-64.so.2 (0x00007f788f3ab000)


REPRODUCTION

If 'chrpath' is now used on our modified 'library.so', bad things happen:

$ chrpath -l library.so
library.so: RUNPATH=_init_array_entry

$ chrpath -r '/tmp' library.so
library.so: RUNPATH=_init_array_entry
library.so: new RUNPATH: /tmp

$ ldd library.so
linux-vdso.so.1 (0x00007ffcfc775000)
library2.so => /var/tmp/library2.so (0x00007facbc55d000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007facbc344000)
/lib64/ld-linux-x86-64.so.2 (0x00007facbc56b000)

$ readelf -d library.so|grep PATH
0x000000000000001d (RUNPATH) Library runpath: [/var/tmp]

$ nm library.so
0000000000004028 b completed.8061
w __cxa_finalize@@GLIBC_2.2.5
0000000000001060 t deregister_tm_clones
00000000000010d0 t __do_global_dtors_aux
0000000000003e18 d __do_global_dtors_aux_fini_array_entry
0000000000004020 d __dso_handle
0000000000003e20 d _DYNAMIC
0000000000001130 t _fini
0000000000001110 t frame_dummy
0000000000003e10 d __frame_dummy/tmp
00000000000020d8 r __FRAME_END__
0000000000004000 d _GLOBAL_OFFSET_TABLE_
w __gmon_start__
0000000000002018 r __GNU_EH_FRAME_HDR
0000000000001000 t _init
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U puts@@GLIBC_2.2.5
0000000000001090 t register_tm_clones
0000000000001119 T test_lib
0000000000004028 d __TMC_END__


Because it interpreted the offset value 'rpathoff' as an offset into the wrong
header section (the '.strtab' table, rather than the '.dynstr' table),
'chrpath' wrote its new RUNPATH value in completely the wrong location in the
header, partially corrupting the name of a different symbol entirely.


The attached patch, against the last available chrpath-0.16 source before the
upstream host went dark, corrects this issue and makes 'chrpath' safe to run on
'patchelf'-modified libraries. Details of the fix are included in the commit
message.


-- System Information:
Debian Release: bullseye/sid
APT prefers focal-updates
APT policy: (500, 'focal-updates'), (500, 'focal-security'), (500, 'focal')
Architecture: amd64 (x86_64)
Foreign Architectures: i386

Kernel: Linux 5.4.0-104-generic (SMP w/4 CPU cores)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8),
LANGUAGE=en_US.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled

Versions of packages chrpath depends on:
ii libc6 2.31-0ubuntu9.9

chrpath recommends no packages.

chrpath suggests no packages.
chrpath-0.16-dynstr_lookup.patch
0 new messages