Segmentation fault in s390x ld.so while parsing /etc/ld.so.cache using qemu-s390x on x86_64.
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
GLibC |
Fix Released
|
Low
|
|||
Ubuntu on IBM z Systems |
Fix Released
|
Medium
|
Skipper Bug Screeners | ||
cross-toolchain-base (Ubuntu) |
Fix Released
|
Medium
|
Unassigned | ||
Focal |
Fix Released
|
Medium
|
Unassigned | ||
glibc (Debian) |
Fix Released
|
Unknown
|
|||
glibc (Ubuntu) |
Fix Released
|
Medium
|
Canonical Foundations Team | ||
Focal |
Fix Released
|
Medium
|
Balint Reczey | ||
Groovy |
Won't Fix
|
Medium
|
Unassigned |
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-
- apt list --installed "libc6*"
libc6/focal-
libc6-s390x-
...
- echo 'int main(void) { puts("Hello, world!"); }' | s390x-linux-gnu-gcc -o helloworld-s390x -x c -
- qemu-s390x -strace -L /usr/s390x-
18392 brk(NULL) = 0x0000004000003000
18392 uname(0x4000803402) = 0
18392 access(
18392 openat(
18392 fstat(3,
18392 mmap(0x00000040
18392 close(3) = 0
--- SIGSEGV {si_signo=SIGSEGV, si_code=1, si_addr=
Segmentation fault (core dumped)
- qemu-s390x -L /usr/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:
...
0x0000004000
0x0000004000
0x0000004000
=> 0x000000400081c572: l %r1,52(%r10,%r6)
0x0000004000
0x0000004000
0x0000004000
0x0000004000
This happens in <glibc>
ld.so.cache is mmaped with:
void *file = _dl_sysdep_
And this check is true:
if (file != MAP_FAILED && cachesize > sizeof *cache_new
&& memcmp (file, CACHEMAGIC_
sizeof CACHEMAGIC_
{
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->
...
(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->
(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:/
I've recognized that the libc6-2.
debian/
which already patches elf/dl-cache.c in _dl_load_
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
--- glibc-2.
+++ glibc-2.
@@ -202,13 +202,16 @@
/* 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_
- sizeof CACHEMAGIC_
+ && memcmp (file, CACHEMAGIC_
+ sizeof CACHEMAGIC_
+ /* 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:/
tags: | added: architecture-s39064 bugnameltc-190005 severity-medium targetmilestone-inin--- |
Changed in ubuntu: | |
assignee: | nobody → Skipper Bug Screeners (skipper-screen-team) |
affects: | ubuntu → linux (Ubuntu) |
affects: | linux (Ubuntu) → glibc (Ubuntu) |
Changed in ubuntu-z-systems: | |
importance: | Undecided → Medium |
assignee: | nobody → Skipper Bug Screeners (skipper-screen-team) |
Changed in glibc (Ubuntu): | |
assignee: | Skipper Bug Screeners (skipper-screen-team) → Canonical Foundations Team (canonical-foundations) |
Changed in ubuntu-z-systems: | |
status: | New → Triaged |
tags: | added: rls-ff-incoming |
tags: | added: fr-974 |
tags: |
added: rls-ff-tracked removed: rls-ff-incoming |
tags: | removed: rls-ff-tracked |
Changed in glibc: | |
importance: | Unknown → Low |
status: | Unknown → In Progress |
Changed in glibc: | |
status: | In Progress → Fix Released |
Changed in glibc (Ubuntu): | |
status: | New → In Progress |
Changed in glibc (Ubuntu Focal): | |
status: | New → In Progress |
assignee: | nobody → Balint Reczey (rbalint) |
Changed in ubuntu-z-systems: | |
status: | Triaged → In Progress |
Changed in glibc (Ubuntu Groovy): | |
status: | New → Fix Released |
Changed in cross-toolchain-base (Ubuntu Groovy): | |
status: | New → Invalid |
tags: |
added: targetmilestone-inin2004 verification-needed verification-needed-focal verification-needed-groovy removed: targetmilestone-inin--- verification-done verification-done-focal verification-done-groovy |
Changed in cross-toolchain-base (Ubuntu Focal): | |
importance: | Undecided → Medium |
Changed in cross-toolchain-base (Ubuntu): | |
importance: | Undecided → Medium |
Changed in glibc (Ubuntu): | |
importance: | Undecided → Medium |
Changed in glibc (Ubuntu Focal): | |
importance: | Undecided → Medium |
Changed in glibc (Ubuntu Groovy): | |
importance: | Undecided → Medium |
Changed in cross-toolchain-base (Ubuntu Groovy): | |
importance: | Undecided → Medium |
Changed in glibc (Debian): | |
status: | Unknown → Confirmed |
tags: |
added: verification-done-groovy removed: verification-needed-groovy |
tags: | added: block-proposed-groovy |
Changed in glibc (Ubuntu): | |
status: | In Progress → Fix Released |
Changed in ubuntu-z-systems: | |
status: | In Progress → Fix Committed |
Changed in cross-toolchain-base (Ubuntu): | |
status: | New → Fix Released |
no longer affects: | cross-toolchain-base (Ubuntu Groovy) |
Changed in glibc (Debian): | |
status: | Confirmed → Fix Released |
tags: | removed: block-proposed-groovy |
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.