bug#58033: A bug in file-dynamic-info used by validate-runpath in gnu-build-system and others.

2022-09-23 Thread Lukasz Olszewski
It appears I found a bug in guix triggered by certain binary data
present in an ELF header.

While running a validate runpath phase of a build-system for a new
package that is not a part of guix I encountered unusual errors
reported for certain binary files. Those binary files RUNPATHs were
modified by patchelf by adding a colon character followed by a
/gnu/store directory folder to runpath. As far as readelf/patchelf and
ld are concerned the binary files appear to contain a valid elf header
with valid DT_DYNAMIC section.

However, something in the ELF header triggers a bug in Guix's
file-runpath / file-dynamic-info procedure that result in the
following output:
scheme@(guix-user)> (file-dynamic-info
"/gnu/store/20z595j5jas5ri3nrza5465gbxwf9kmf-python-redacted/lib/python3.9/site-packages/torch/bin/FileStoreTest")
$13 = #< soname: #f needed: ("" "" "" "" "" "" "" ""
"" "" "" "" "" "" "") rpath: () runpath: ()>
scheme@(guix-user)>

As can be seen above file-dynamic-info is unable to read the NEEDED
items, but correctly reports 8 of them. Additionally it is unable to
read the RUNPATH variable and returns an empty string. All those
values are populated in the header as shown below.

readelf -d reports the following correct DT_DYNAMIC:
[luk@archczop guix]$ readelf -d
/gnu/store/20z595j5jas5ri3nrza5465gbxwf9kmf-python-redacted/lib/python3.9/site-packages/torch/bin/FileStoreTest

Dynamic section at offset 0xfcf0 contains 40 entries:
  TagType Name/Value
 0x0001 (NEEDED) Shared library: [libtorch_cpu.so]
 0x0001 (NEEDED) Shared library:
[libgtest_main.so.1.11.0]
 0x0001 (NEEDED) Shared library: [libgtest.so.1.11.0]
 0x0001 (NEEDED) Shared library: [libpthread.so.0]
 0x0001 (NEEDED) Shared library: [libprotobuf.so.28]
 0x0001 (NEEDED) Shared library: [libc10.so]
 0x0001 (NEEDED) Shared library:
[libmkl_intel_lp64.so.2]
 0x0001 (NEEDED) Shared library:
[libmkl_gnu_thread.so.2]
 0x0001 (NEEDED) Shared library: [libmkl_core.so.2]
 0x0001 (NEEDED) Shared library: [libdl.so.2]
 0x0001 (NEEDED) Shared library: [libstdc++.so.6]
 0x0001 (NEEDED) Shared library: [libm.so.6]
 0x0001 (NEEDED) Shared library: [libgomp.so.1]
 0x0001 (NEEDED) Shared library: [libgcc_s.so.1]
 0x0001 (NEEDED) Shared library: [libc.so.6]
 0x001d (RUNPATH)Library runpath:
[$ORIGIN/../lib:/gnu/store/5h2w4qi9hk1qzzgi1w83220ydslinr4s-glibc-2.33/lib:/gnu/store/094bbaq6glba86h1d4cj16xhdi6fk2jl-gcc-10.3.0-lib/lib:/gnu/store/mbzav28sik3zr3kbw1jyh4qk3zmkh6xn-googletest-1.11.0/lib:/gnu/store/9pyydl5w9xnz1qm56sxn1zh4qny6fkxz-protobuf-3.17.3/lib:/gnu/store/fj5npv9kpsiihrzpzhdlcz5q6bax15s8-mkl-2022.1.0/lib:/gnu/store/094bbaq6glba86h1d4cj16xhdi6fk2jl-gcc-10.3.0-lib/lib/gcc/x86_64-unknown-linux-gnu/10.3.0/../../..:/gnu/store/janq8zcngwc7120gyj41cc2yysk7p9i5-nvidia-libs-515.65.01/lib]
 0x000c (INIT)   0x405000
 0x000d (FINI)   0x40bed4
 0x0019 (INIT_ARRAY) 0x40f6c8
 0x001b (INIT_ARRAYSZ)   16 (bytes)
 0x001a (FINI_ARRAY) 0x40f6d8
 0x001c (FINI_ARRAYSZ)   8 (bytes)
 0x0004 (HASH)   0x402210
 0x6ef5 (GNU_HASH)   0x401fa8
 0x0005 (STRTAB) 0x3ff350
 0x0006 (SYMTAB) 0x4011b0
 0x000a (STRSZ)  7769 (bytes)
 0x000b (SYMENT) 24 (bytes)
 0x0015 (DEBUG)  0x0
 0x0003 (PLTGOT) 0x41
 0x0002 (PLTRELSZ)   1680 (bytes)
 0x0014 (PLTREL) RELA
 0x0017 (JMPREL) 0x4039e0
 0x0007 (RELA)   0x403710
 0x0008 (RELASZ) 720 (bytes)
 0x0009 (RELAENT)24 (bytes)
 0x6ffe (VERNEED)0x403620
 0x6fff (VERNEEDNUM) 4
 0x6ff0 (VERSYM) 0x4034f0
 0x (NULL)   0x0

Therefore deeper analysis of what is in the binary header that
triggers the bug is required. I have a number of those binary files. I
attached the smallest one(74kB) base64 encoded to this email. It is
the binary named FileStoreTest used in the report above. If it gets
stripped from the message I'll reply by submitting it in the body of
the message.


FileStoreTest.b64
Description: Binary data


bug#58033: A bug in file-dynamic-info used by validate-runpath in gnu-build-system and others.

2022-09-25 Thread Lukasz Olszewski
After further troubleshooting it appears the elf binary might be
malformed. I've tried to run the executable on another machine and ld
complained like this:
$ ./FileStoreTest
./FileStoreTest: ./FileStoreTest: no version information available
(required by ./FileStoreTest)
./FileStoreTest: ./FileStoreTest: no version information available
(required by ./FileStoreTest)
./FileStoreTest: ./FileStoreTest: no version information available
(required by ./FileStoreTest)
./FileStoreTest: ./FileStoreTest: no version information available
(required by ./FileStoreTest)
./FileStoreTest: ./FileStoreTest: no version information available
(required by ./FileStoreTest)
./FileStoreTest: ./FileStoreTest: no version information available
(required by ./FileStoreTest)
./FileStoreTest: ./FileStoreTest: no version information available
(required by ./FileStoreTest)
./FileStoreTest: ./FileStoreTest: no version information available
(required by ./FileStoreTest)
./FileStoreTest: ./FileStoreTest: no version information available
(required by ./FileStoreTest)
./FileStoreTest: ./FileStoreTest: no version information available
(required by ./FileStoreTest)
./FileStoreTest: ./FileStoreTest: no version information available
(required by ./FileStoreTest)
./FileStoreTest: symbol lookup error: ./FileStoreTest: undefined
symbol: , version

Further testing is needed to tell if this is indeed a bug in guix not
processing a correct header, or simply failing to display a meaningful
error message.

Regards,
Lukasz





bug#58033: A bug in file-dynamic-info used by validate-runpath in gnu-build-system and others.

2022-09-25 Thread Lukasz Olszewski
Also, to ensure all the information is provided. This is the code that
resulted in the binary header being transformed:

(add-after 'install 'fix-issue-with-libs
  (lambda* (#:key inputs outputs #:allow-other-keys)
(chdir "..")
(use-modules (ice-9 ftw)
(ice-9 regex)
(ice-9 rdelim)
(ice-9 popen)
(ice-9 textual-ports))
(let* ((libdir (string-append #$output "/lib")))
  ;; --
  ;; patchelf
  (define (get-rpaths file)
(format #t "Getting rpaths from ~a ...~%" file)
(let* ((port (open-input-pipe (string-append "patchelf --print-rpath " file)))
   (str  (read-line port))) ; from (ice-9 rdelim)
  (close-pipe port)
  str))
  (define (patch-elf file)
  (format #t "Patching ~a ...~%" file)
(define rpath (string-append (get-rpaths file) ":" #$extra-libs "/lib"))
(display (string-append "We're setting rpath:" rpath))
(invoke "patchelf" "--set-rpath" rpath file))
  (for-each (lambda (file)
  (when (elf-file? file)
(patch-elf file)))
(find-files #$output  ".*")

I can run the patch-elf procedure in repl and it runs fine, but being
run during the build it results in the problematic elf header. The
build was run twice with the same result both times.

Regards,
Lukasz





bug#58033: A bug in file-dynamic-info used by validate-runpath in gnu-build-system and others.

2022-09-27 Thread Lukasz Olszewski
Sorry, for multiple emails with the same content. There was a delay of
over 10h in those emails showing up and I thought they were not
getting through.

Regarding the issue. It seems it has to do with the strip phase. I
managed to replicate the whole issue outside the build system by
taking binaries backed up after install.

Then if I run $ strip --strip-unneeded --enable-deterministic-archives
file the files can be run fine, but if I use patchelf to add an extra
folder to the rpath strip complains like this:
$ strip --strip-unneeded --enable-deterministic-archives
/home/luk/dev/backup_FileStoreTest
strip: /home/luk/dev/stt5WKN1: warning: allocated section `.dynstr'
not in segment

Then the binary has its elf header mangled as described previously.

By copying the unmodified file, modifying rpath and running strip a
couple of times I found that there is no problem if the rpath change
results in rpath of the same or shorter length, but adding even one
byte to it makes 'strip' later complain and mangle the binary.

Regards,
Lukasz





bug#58033: A bug in file-dynamic-info used by validate-runpath in gnu-build-system and others.

2022-10-07 Thread Ludovic Courtès
Hi Lukasz,

Lukasz Olszewski  skribis:

> Then if I run $ strip --strip-unneeded --enable-deterministic-archives
> file the files can be run fine, but if I use patchelf to add an extra
> folder to the rpath strip complains like this:
> $ strip --strip-unneeded --enable-deterministic-archives
> /home/luk/dev/backup_FileStoreTest
> strip: /home/luk/dev/stt5WKN1: warning: allocated section `.dynstr'
> not in segment
>
> Then the binary has its elf header mangled as described previously.
>
> By copying the unmodified file, modifying rpath and running strip a
> couple of times I found that there is no problem if the rpath change
> results in rpath of the same or shorter length, but adding even one
> byte to it makes 'strip' later complain and mangle the binary.

I believe PatchELF has the potential to break binaries, especially when
trying to extend RUNPATH (the data has to fit in the string table;
PatchELF is supposed to be able to grow the string table as needed, but
there might be bugs.)

It looks like a workaround is to not run ‘strip’, right?

Ludo’.