[Bug 1906250] [NEW] Segmentation fault in s390x ld.so while parsing /etc/ld.so.cache using qemu-s390x on x86_64.

Launchpad Bug Tracker 1906250 at bugs.launchpad.net
Mon Nov 30 10:56:39 UTC 2020


You have been subscribed to a public bug:

---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

** Affects: glibc (Ubuntu)
     Importance: Undecided
     Assignee: Skipper Bug Screeners (skipper-screen-team)
         Status: New


** Tags: architecture-s39064 bugnameltc-190005 severity-medium targetmilestone-inin---
-- 
Segmentation fault in s390x ld.so while parsing /etc/ld.so.cache using qemu-s390x on x86_64.
https://bugs.launchpad.net/bugs/1906250
You received this bug notification because you are a member of Ubuntu Foundations Bugs, which is subscribed to glibc in Ubuntu.



More information about the foundations-bugs mailing list