Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Corruption with program headers #243

Closed
wants to merge 2 commits into from
Closed

Conversation

eytanaim
Copy link

This corruption doesn't affect runtime linking but can be observed with inspection tools like ldd and readelf (probably because dynamic linker doesn't really go over all the program headers of shared object - only a theory)

Please use the following library for reproduction: libm.so.6.zip

eytannaim@X270-172806:/mnt/c/work/git/patchelf$ cp ../../../tmp/libm.so.6  ./
eytannaim@X270-172806:/mnt/c/work/git/patchelf$ ./src/patchelf --add-needed libpthread.so.0  ./libm.so.6
eytannaim@X270-172806:/mnt/c/work/git/patchelf$ LD_PRELOAD=./libm.so.6 sleep 1    --------> WORKS!!!!
eytannaim@X270-172806:/mnt/c/work/git/patchelf$ ldd ./libm.so.6
ldd: exited with unknown exit code (139)
eytannaim@X270-172806:/mnt/c/work/git/patchelf$ readelf  -a ./libm.so.6 > /dev/null
readelf: Error: the PHDR segment is not covered by a LOAD segment

The root cause of this issue is similar to the one of #224 or #239
Thank you!

@domenkozar
Copy link
Member

Is there a way to compile a simple program to reproduce this so it can be added to regression tests?

@eytanaim
Copy link
Author

I couldn't find a special linker flag that causes this to break. I found out that many standard shared objects (like pthread, libm, libdl...) from the glibc package on both rhel6 and rhel5.

@domenkozar
Copy link
Member

@edolstra do you think it's fine in that case to vendor the library?

@ovpanait
Copy link
Contributor

Hi!

I also encountered this issue with the following binaries:

repro.tar.gz
ld-linux-x86-64.so.2.tar.gz

$ ./repro
2021/05/18 16:07:23 no config file specified
$ ./src/patchelf --set-interpreter ./ld-linux-x86-64.so.2 repro
patching ELF file 'repro'
replacing section '.interp' with size 23
this is a dynamic library
last page is 0x1fb1000
needed space is 24
shifting new PT_LOAD segment by 2768896 bytes to work around a Linux kernel bug
rewriting section '.interp' from offset 0xf8a (size 118) to offset 0x2255000 (size 23)
rewriting symbol table section 13
rewriting symbol table section 34
writing repro
$ ./repro
Segmentation fault
$ readelf -a repro > /dev/null
readelf: Warning: [ 0]: Link field (24) should index a string section.
readelf: Error: the PHDR segment is not covered by a LOAD segment

Note: #270 patch needs to be applied, otherwise the issue is not reproducible since commit dd4d2af was merged.

If commit 4a61b24 is rebased on top of current master, the issue is no longer present:

diff --git a/src/patchelf.cc b/src/patchelf.cc
index 5c8b5c0..0e2ecfd 100644
--- a/src/patchelf.cc
+++ b/src/patchelf.cc
@@ -772,12 +772,15 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary()
        PT_LOAD segment located directly after the last virtual address
        page of other segments. */
     Elf_Addr startPage = 0;
+    Elf_Addr firstPage = 0;
     for (auto & phdr : phdrs) {
         Elf_Addr thisPage = roundUp(rdi(phdr.p_vaddr) + rdi(phdr.p_memsz), getPageSize());
         if (thisPage > startPage) startPage = thisPage;
+        if (rdi(phdr.p_type) == PT_PHDR) firstPage = rdi(phdr.p_vaddr) - rdi(phdr.p_offset);
     }
 
     debug("last page is 0x%llx\n", (unsigned long long) startPage);
+    debug("first page is 0x%llx\n", (unsigned long long) firstPage);
 
     /* When normalizing note segments we will in the worst case be adding
        1 program header for each SHT_NOTE section. */
@@ -848,7 +851,7 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary()
     assert(curOff == startOffset + neededSpace);
 
     /* Write out the updated program and section headers */
-    rewriteHeaders(rdi(hdr->e_phoff));
+    rewriteHeaders(firstPage + hdr->e_phoff);
 }

Output after rebuilding patchelf:

$ ./repro
2021/05/18 16:13:42 no config file specified
$ ./src/patchelf --set-interpreter ./ld-linux-x86-64.so.2 repro
patching ELF file 'repro'
replacing section '.interp' with size 23
this is a dynamic library
last page is 0x1fb1000
first page is 0x400000
needed space is 24
shifting new PT_LOAD segment by 2768896 bytes to work around a Linux kernel bug
rewriting section '.interp' from offset 0xf8a (size 118) to offset 0x2255000 (size 23)
rewriting symbol table section 13
rewriting symbol table section 34
writing repro
$ ./repro
2021/05/18 16:13:52 no config file specified
$ readelf -a repro > /dev/null
readelf: Warning: [ 0]: Link field (24) should index a string section.

Could you please let me know how I could help in order to proceed with the integration?

Thanks!
Ovidiu

@domenkozar
Copy link
Member

If someone opens a PR with rebased version and with repro file added as a test, it has a higher chance of being merged.

@satmandu
Copy link
Contributor

@ovpanait Is there source for that repro file? This is what I get when I run this:

#!/bin/sh
# PR243-reproducer.sh
set -ex
curl -OLf https://github.com/NixOS/patchelf/files/6501509/ld-linux-x86-64.so.2.tar.gz
curl -OLf https://github.com/NixOS/patchelf/files/6501457/repro.tar.gz
tar fx repro.tar.gz
tar fx ld-linux-x86-64.so.2.tar.gz
chmod +x repro
cp repro repro.orig
../src/patchelf --set-interpreter ./ld-linux-x86-64.so.2 ./repro
patchelf --print-interpreter repro.orig 
readelf -a repro > /dev/null
./repro
./PR243-reproducer.sh
+ curl -OLf https://github.com/NixOS/patchelf/files/6501509/ld-linux-x86-64.so.2.tar.gz
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   599  100   599    0     0   3608      0 --:--:-- --:--:-- --:--:--  3608
100 85356  100 85356    0     0   184k      0 --:--:-- --:--:-- --:--:--  184k
+ curl -OLf https://github.com/NixOS/patchelf/files/6501457/repro.tar.gz
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   584  100   584    0     0   2703      0 --:--:-- --:--:-- --:--:--  2691
100 12.7M  100 12.7M    0     0  8501k      0  0:00:01  0:00:01 --:--:-- 13.8M
+ tar fx repro.tar.gz
+ tar fx ld-linux-x86-64.so.2.tar.gz
+ chmod +x repro
+ cp repro repro.orig
+ ../src/patchelf --set-interpreter ./ld-linux-x86-64.so.2 ./repro
+ patchelf --print-interpreter repro.orig
/buildarea/raid0/ovidiu/Builds/syzkaller-wrl/build/tmp-glibc/sysroots-uninative/x86_64-linux/lib/ld-linux-x86-64.so.2
+ readelf -a repro
readelf: Warning: [ 0]: Link field (24) should index a string section.
+ ./repro
*** stack smashing detected ***: <unknown> terminated
Aborted (core dumped)

(So I guess this reproducer works?)

@ovpanait
Copy link
Contributor

@ovpanait Is there source for that repro file? This is what I get when I run this:

The repro file is generated by the go build system in a cross-compilation environment and so far I was not able to create a manual reproducer in C.

#!/bin/sh
# PR243-reproducer.sh
set -ex
curl -OLf https://github.com/NixOS/patchelf/files/6501509/ld-linux-x86-64.so.2.tar.gz
curl -OLf https://github.com/NixOS/patchelf/files/6501457/repro.tar.gz
tar fx repro.tar.gz
tar fx ld-linux-x86-64.so.2.tar.gz
chmod +x repro
cp repro repro.orig
../src/patchelf --set-interpreter ./ld-linux-x86-64.so.2 ./repro
patchelf --print-interpreter repro.orig 
readelf -a repro > /dev/null
./repro

Maybe the testcase should just check readelf output for "the PHDR segment is not covered by a LOAD segment" error rather than running the prebuilt binary? (since it's an x86_64 binary that will not run on other arches)

@domenkozar
Copy link
Member

Fixed in #278

@domenkozar domenkozar closed this Aug 5, 2021
ovpanait added a commit to ovpanait/patchelf that referenced this pull request Sep 26, 2021
A PT_PHDR corruption was previously reported and fixed in [1]: the issue was
that the VirtAddr field of the PT_PHDR program header would get overwritten
with the file offset of the program header table rather than the virtual
address. A testcase for this was also added in [2]. However, the tescase is not
included in the Makefile.am regression testsuite and also tries to run a x86_64
prebuilt binary unconditionally, which would not work on other architectures.

To fix this, create a standalone testcase for the PT_PHDR VirtAddr field
corruption and include it in Makefile.am. In order to reproduce [1], a binary
with the following characteristics is needed:
- the ELF file type must be ET_DYN
- the ELF file must contain a PT_PHDR program header
- the file offset and the VirtAddr field of the PT_PHDR program header must be
  different

[1] NixOS#243
[2] NixOS@8f94e11

Signed-off-by: Ovidiu Panait <ovpanait@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants