[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