[Bug 1906250] Re: Segmentation fault in s390x ld.so while parsing /etc/ld.so.cache using qemu-s390x on x86_64.
Bug Watch Updater
1906250 at bugs.launchpad.net
Fri Dec 4 01:35:51 UTC 2020
Launchpad has imported 2 comments from the remote bug at
https://sourceware.org/bugzilla/show_bug.cgi?id=27008.
If you reply to an imported comment from within Launchpad, your comment
will be sent to the remote bug automatically. Read more about
Launchpad's inter-bugtracker facilities at
https://help.launchpad.net/InterBugTracking.
------------------------------------------------------------------------
On 2020-12-03T22:32:05+00:00 Florian Weimer wrote:
With qemu-user, it's common that a process of the wrong endianness tries
to parse ld.so.cache. For performance reasons, the consistency checks
are somewhat limited, so crashes can be the result.
Reply at:
https://bugs.launchpad.net/ubuntu/+source/glibc/+bug/1906250/comments/1
------------------------------------------------------------------------
On 2020-12-03T22:35:12+00:00 Florian Weimer wrote:
Patch: https://sourceware.org/pipermail/libc-
alpha/2020-November/119447.html
Reply at:
https://bugs.launchpad.net/ubuntu/+source/glibc/+bug/1906250/comments/2
** Changed in: glibc
Status: Unknown => In Progress
** Changed in: glibc
Importance: Unknown => Low
--
You received this bug notification because you are a member of Ubuntu
Foundations Bugs, which is subscribed to glibc in Ubuntu.
https://bugs.launchpad.net/bugs/1906250
Title:
Segmentation fault in s390x ld.so while parsing /etc/ld.so.cache using
qemu-s390x on x86_64.
Status in GLibC:
In Progress
Status in Ubuntu on IBM z Systems:
Triaged
Status in glibc package in Ubuntu:
New
Status in glibc source package in Focal:
New
Bug description:
---Problem Description---
On a x86_64 machine with Ubuntu 20.04, running a s390x (or ppc64) binary with qemu leads to a segmentation fault in ld.so while lookup in /etc/ld.so.cache.
Contact Information = via bugzilla
---uname output---
Linux 5.4.0-54-generic #60-Ubuntu SMP Fri Nov 6 10:37:59 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
---Steps to Reproduce---
- apt-get install -y --no-install-recommends gcc-s390x-linux-gnu libc6-dev-s390x-cross qemu-user
- apt list --installed "libc6*"
libc6/focal-updates,now 2.31-0ubuntu9.1 amd64 [installed,automatic]
libc6-s390x-cross/focal,focal,now 2.31-0ubuntu7cross1 all [installed,automatic]
...
- echo 'int main(void) { puts("Hello, world!"); }' | s390x-linux-gnu-
gcc -o helloworld-s390x -x c -
- qemu-s390x -strace -L /usr/s390x-linux-gnu ./helloworld-s390x
18392 brk(NULL) = 0x0000004000003000
18392 uname(0x4000803402) = 0
18392 access("/etc/ld.so.preload",R_OK) = -1 errno=2 (No such file or directory)
18392 openat(AT_FDCWD,"/etc/ld.so.cache",O_RDONLY|O_CLOEXEC) = 3
18392 fstat(3,0x0000004000802720) = 0
18392 mmap(0x0000004000802648,65784,PROT_READ,MAP_PRIVATE,3,0x82d140) = 0x000000400082e000
18392 close(3) = 0
--- SIGSEGV {si_signo=SIGSEGV, si_code=1, si_addr=0x0000004300a6e000} ---
Segmentation fault (core dumped)
- qemu-s390x -L /usr/s390x-linux-gnu -g 12345 ./helloword-s390x
- gdb-multiarch ./helloword-s390x
target remote localhost:12345
c
Program received signal SIGSEGV, Segmentation fault.
0x000000400081c572 in ?? ()
Dump of assembler code from 0x400081c500 to 0x400081c600:
...
0x000000400081c564: l %r4,216(%r11)
0x000000400081c568: agr %r10,%r1
0x000000400081c56c: sllg %r10,%r10,3
=> 0x000000400081c572: l %r1,52(%r10,%r6)
0x000000400081c576: lgdr %r2,%f8
0x000000400081c57a: algfr %r3,%r1
0x000000400081c57e: clrjnh %r4,%r1,0x400081c5a0
0x000000400081c584: brasl %r14,0x400081c290
This happens in <glibc>/elf/dl-cache.c:_dl_load_cache_lookup():
ld.so.cache is mmaped with:
void *file = _dl_sysdep_read_whole_file (LD_SO_CACHE, &cachesize, PROT_READ);
And this check is true:
if (file != MAP_FAILED && cachesize > sizeof *cache_new
&& memcmp (file, CACHEMAGIC_VERSION_NEW,
sizeof CACHEMAGIC_VERSION_NEW - 1) == 0)
{
cache_new = file;
cache = file;
}
The segmentation fault happens in SEARCH_CACHE macro which is also defined in elf/dl-cache.c:
if (cache_new != (void *) -1)
{
...
SEARCH_CACHE (cache_new);
}
#define SEARCH_CACHE(cache)
...
left = 0;
right = cache->nlibs - 1;
middle = (left + right) / 2;
key = cache->libs[middle].key;
...
(gdb) p/x *((struct cache_file_new *) $r6)
$5 = {magic = {0x67, 0x6c, 0x69, 0x62, 0x63, 0x2d, 0x6c, 0x64, 0x2e, 0x73, 0x6f, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65}, version = {0x31, 0x2e,
0x31}, nlibs = 0x5e000000, len_strings = 0x48130000, unused = {0x0, 0x0, 0x0, 0x0, 0x0}, libs = 0x3fffdf00030}
(gdb) ptype cache_new
type = struct cache_file_new {
char magic[17];
char version[3];
uint32_t nlibs;
uint32_t len_strings;
uint32_t unused[5];
struct file_entry_new libs[0];
} *
As /etc/ld.so.cache is generated by x86_64 (little endian) code, we
get a huge number for nlibs on s390x (big endian).
The segfault happens while:
l %r1,52(%r10,%r6)
=> key = cache->libs[middle].key;
(gdb) i r r6
r6 0x3fffdf00000 => cache_new
(gdb) p &(((struct cache_file_new *) $r6)->libs[0])
$17 = (struct file_entry_new *) 0x3fffdf00030
(gdb) p &(((struct cache_file_new *) $r6)->libs[0].key)
$18 = (uint32_t *) 0x3fffdf00034
=> 0x3fffdf00034 - 0x3fffdf00000 = 0x34 = 52
On glibc upstream > glibc-2.31 && < glibc-2.32,
there is the following commit which adds a further check for corruption, avoiding overflow:
"ld.so: Check for new cache format first and enhance corruption check"
https://sourceware.org/git/?p=glibc.git;a=commit;h=e221c512c74ec42fd47b71de2981a475b38110a4
I've recognized that the libc6-2.31-0ubuntu9.1 package contains the patch
debian/patches/any/submitted-ld.so-cache-new-format.diff
which already patches elf/dl-cache.c in _dl_load_cache_lookup().
Therefore the mentioned commit does not apply.
For testing, I've added this patch and just rebuild the libc6-s390x-cross package:
cat glibc-ldsocache-corruption.diff
--- glibc-2.31/elf/dl-cache.c 2020-11-26 15:36:33.963032580 +0100
+++ glibc-2.31/elf/dl-cache.c 2020-11-26 15:39:13.866894100 +0100
@@ -202,13 +202,16 @@
PROT_READ);
/* We can handle three different cache file formats here:
+ - only the new format
- the old libc5/glibc2.0/2.1 format
- the old format with the new format in it
- - only the new format
The following checks if the cache contains any of these formats. */
if (file != MAP_FAILED && cachesize > sizeof *cache_new
- && memcmp (file, CACHEMAGIC_VERSION_NEW,
- sizeof CACHEMAGIC_VERSION_NEW - 1) == 0)
+ && memcmp (file, CACHEMAGIC_VERSION_NEW,
+ sizeof CACHEMAGIC_VERSION_NEW - 1) == 0
+ /* Check for corruption, avoiding overflow. */
+ && ((cachesize - sizeof *cache_new) / sizeof (struct file_entry_new)
+ >= ((struct cache_file_new *) file)->nlibs))
{
cache_new = file;
cache = file;
Now the additional check leads to unmapping ld.so.cache and ignoring the content of ld.so.cache.
The hello-world program is now working fine.
Please add this patch to libc6 package and also rebuild the
libc6-*cross packages.
Just as reference:
"Debian Bug report logs - #731082 ld.so.cache parsing code does not deal with mixed endianess multiarch, causing segfaults"
Date: Sun, 1 Dec 2013 19:30:01 UTC
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=731082
To manage notifications about this bug go to:
https://bugs.launchpad.net/glibc/+bug/1906250/+subscriptions
More information about the foundations-bugs
mailing list