Public bug reported:

libelf from elfutils 0.196-1 corrupts an ELF file when it rewrites it
with elf_update(ELF_C_WRITE) in ELF_C_RDWR (non-mmap) mode with
ELF_F_LAYOUT set, if the section header table is not at the end of the
file. All section headers in the output end up zeroed (readelf shows
<no-strings>/NULL for every section, "no .dynamic section in the dynamic
segment"), and objcopy then fails with "has a corrupt string table
index".

patchelf always produces that layout: e.g. after "patchelf --set-
soname", the moved .note.gnu.build-id/.gnu.hash/.dynstr/.dynamic
sections are placed in a new segment after the section header table.

debugedit opens files with ELF_C_RDWR and sets ELF_F_LAYOUT
(tools/debugedit.c), so "debugedit --build-id --build-id-seed=X" (which
rewrites the build-id note, forcing a write) corrupts any patchelf-
modified file. Without a seed debugedit does not write and nothing
happens. The same write with ELF_C_RDWR_MMAP works fine, and so does
debugedit on a copy rewritten by objcopy (which puts the section header
table at the end).

Reproducer (stonking, libelf1t64 0.196-1, debugedit 1:5.3-4, patchelf 
0.18.0-1.4build1), with any shared library that has a build-id:
  patchelf --set-soname libx.so.0 --output p.so libfoo.so
  cp p.so r.so; debugedit --build-id --build-id-seed=x r.so
  readelf -SW r.so      # all section headers are NULL/<no-strings>
  objcopy --only-keep-debug r.so r.dbg   # "corrupt string table index"

Minimal libelf reproducer (flip a byte of the build-id note and write
back):

#include <libelf.h>
#include <gelf.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(int c,char**v){ int mm=c>2; elf_version(EV_CURRENT); int 
fd=open(v[1],O_RDWR);
 Elf*e=elf_begin(fd, mm?ELF_C_RDWR_MMAP:ELF_C_RDWR,NULL); 
elf_flagelf(e,ELF_C_SET,ELF_F_LAYOUT);
 size_t shstr; elf_getshdrstrndx(e,&shstr); Elf_Scn*s=NULL;
 while((s=elf_nextscn(e,s))){GElf_Shdr h;gelf_getshdr(s,&h); 
if(!strcmp(elf_strptr(e,shstr,h.sh_name),".note.gnu.build-id")){Elf_Data*d=elf_getdata(s,NULL);((char*)d->d_buf)[20]^=1;elf_flagdata(d,ELF_C_SET,ELF_F_DIRTY);}}
 if(elf_update(e,ELF_C_WRITE)<0)printf("err %s\n",elf_errmsg(-1)); elf_end(e); 
close(fd); return 0;}

  gcc t.c -lelf -o t
  cp p.so u.so; ./t u.so        -> 39 NULL section headers
  cp p.so u.so; ./t u.so mmap   -> file is fine

In Ubuntu this matters because debhelper's Ubuntu delta runs "debugedit
--build-id --build-id-seed=..." on every ELF file in dh_strip, so
packages shipping patchelf-modified files fail to build (see the
debhelper bug cross-referenced in a comment). Debian is not affected in
practice because its dh_strip does not call debugedit.

Not checked: whether elfutils upstream already fixed this (sourceware
git was not reachable from here), or whether debugedit should rather
open with ELF_C_RDWR_MMAP.

** Affects: debugedit (Ubuntu)
     Importance: Undecided
         Status: New

** Affects: elfutils (Ubuntu)
     Importance: Undecided
         Status: New

-- 
You received this bug notification because you are a member of Ubuntu
Bugs, which is subscribed to Ubuntu.
https://bugs.launchpad.net/bugs/2169676

Title:
  libelf elf_update(ELF_C_WRITE) with ELF_F_LAYOUT corrupts section
  headers when the section header table is not at the end of the file
  (patchelf output); breaks debugedit

To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/debugedit/+bug/2169676/+subscriptions


-- 
ubuntu-bugs mailing list
[email protected]
https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs

Reply via email to