dl_open segment fault in ubuntu18.10 glibc2.28
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
glibc (Ubuntu) |
Fix Released
|
High
|
Adam Conrad | ||
Bionic |
Fix Released
|
Undecided
|
Unassigned | ||
Cosmic |
Won't Fix
|
Undecided
|
Unassigned | ||
Disco |
Fix Released
|
High
|
Adam Conrad |
Bug Description
[Impact]
* Dlopen() may crash.
[Test Case]
$ sudo apt install make gcc
$ wget https:/
$ tar -xf dl-big-note.tar.xz
$ cd dl-big-note/
$ make
$ ./dl-big-note dl-big-note-lib.so
all ok
[Where problems could occur]
* The fix is correcting a patch that was not updated to the new upstream code that was backported. There is little change in the code, but in case of an error it can crash again, let dlopen load an invalid ELF file due to the false positive verification or reject a valid ELF file due to erroneoudly failing verification (least likely).
[Other Info]
I've tested the fix with an amd64-only build and I'm building the packages here for all arches:
https:/
[Original Bug Text]
With following testcase:
~/work/glibc$ cat foo.c
#include <dlfcn.h>
#include <stdio.h>
int main(int argc, char **argv) {
if (argc < 1) return 1;
printf("Trying to open %s\n", argv[1]);
void *liball = dlopen(argv[1], RTLD_NOW);
if(liball == NULL) {
printf(
return -1;
}
if(dlclose(
return 0;
}
compile with
~/work/glibc$ gcc -O0 -g foo.c -ldl
then get segment fault:
~/work/glibc$ ./a.out intel64_
Trying to open intel64_
Segmentation fault (core dumped)
coredump as:
(gdb) bt
#0 __GI___libc_free (mem=0x7ffff7d4
#1 0x00007ffff7fdb6b6 in open_verify (
name=
fbp=
mode=
found_
whatcode=0, fd=3) at dl-load.c:1977
#2 0x00007ffff7fdc926 in _dl_map_object (loader=
name=
type=
mode=
#3 0x00007ffff7fe79c4 in dl_open_worker (a=a@entry=
at dl-open.c:228
#4 0x00007ffff7f1b48f in __GI__dl_
operate=
#5 0x00007ffff7fe72c6 in _dl_open (
file=
mode=
nsid=<optimized out>, argc=2, argv=0x7fffffff
at dl-open.c:599
#6 0x00007ffff7faa256 in dlopen_doit (a=a@entry=
#7 0x00007ffff7f1b48f in __GI__dl_
exception=
--Type <RET> for more, q to quit, c to continue without paging--
args=<optimized out>) at dl-error-
#8 0x00007ffff7f1b51f in __GI__dl_
objname=
errstring=
mallocedp=
args=<optimized out>) at dl-error-
#9 0x00007ffff7faaa25 in _dlerror_run (
operate=
args=
#10 0x00007ffff7faa2e6 in __dlopen (file=<optimized out>, mode=<optimized out>)
at dlopen.c:87
#11 0x00005555555551cb in main (argc=2, argv=0x7fffffff
intel64_
Ubuntu version:
~/work/glibc$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 18.10
Release: 18.10
Codename: cosmic
Glibc version:
~/work/glibc$ ldd --version
ldd (Ubuntu GLIBC 2.28-0ubuntu1) 2.28
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.
It works fine with Glibc_2.28 upstream, and Glibc_2.28 on Fedora 29, but failed with Glibc 2.28 in Ubuntu 18.10
I found ubuntu18.10 was backporting its own patches, would that affect such testcase?
information type: | Public → Public Security |
information type: | Public Security → Public |
Changed in glibc (Ubuntu): | |
importance: | Undecided → High |
tags: | added: rls-dd-incoming |
tags: | added: rls-cc-incoming |
tags: | removed: rls-cc-incoming rls-dd-incoming |
tags: | added: id-5ca6214756f1b84f8df62a00 |
Changed in glibc (Ubuntu Disco): | |
assignee: | nobody → Adam Conrad (adconrad) |
status: | New → Fix Committed |
Changed in glibc (Ubuntu Bionic): | |
status: | New → Confirmed |
tags: | added: regression-update rls-bb-incoming |
Changed in glibc (Ubuntu Cosmic): | |
status: | Confirmed → Fix Released |
description: | updated |
This regression caused by following patch. It is mostly arm code but also affecting x86. If I remove this patch segfault will go away.
$ cat unsubmitted- ldso-abi- check.diff +++++++ +++++++ +++++++ +++++++ +++++++ +++++++ +++++++ ++
---
elf/dl-load.c | 219 +++++++
1 file changed, 219 insertions(+)
--- a/elf/dl-load.c debug_printf_ c ("\t\t(%s)\n", what);
+++ b/elf/dl-load.c
@@ -1438,6 +1438,209 @@
_dl_
}
+#ifdef __arm__
+/* Read an unsigned leb128 value from P, store the value in VAL, return
+ P incremented past the value. We assume that a word is large enough to
+ hold any value so encoded; if it is smaller than a pointer on some target,
+ pointers should not be leb128 encoded on that target. */
+static unsigned char *
+read_uleb128 (unsigned char *p, unsigned long *val)
+{
+ unsigned int shift = 0;
+ unsigned char byte;
+ unsigned long result;
+
+ result = 0;
+ do
+ {
+ byte = *p++;
+ result |= (byte & 0x7f) << shift;
+ shift += 7;
+ }
+ while (byte & 0x80);
+
+ *val = result;
+ return p;
+}
+
+
+#define ATTR_TAG_FILE 1 IN_VFP_ REGS 1 arm_attributes_ hfabi(int fd, ElfW(Ehdr) *ehdr, bool *is_hf) shdrs[i] .sh_size) ;
+#define ABI_VFP_args 28
+#define VFP_ARGS_
+
+/* Check consistency of ABI in the ARM attributes. Search through the
+ section headers looking for the ARM attributes section, then
+ check the VFP_ARGS attribute. */
+static int
+check_
+{
+ unsigned int i;
+ ElfW(Shdr) *shdrs;
+ int sh_size = ehdr->e_shentsize * ehdr->e_shnum;
+
+ /* Load in the section headers so we can look for the attributes
+ * section */
+ shdrs = alloca(sh_size);
+ __lseek (fd, ehdr->e_shoff, SEEK_SET);
+ if ((size_t) __libc_read (fd, (void *) shdrs, sh_size) != sh_size)
+ return -1;
+
+ for (i = 0; i < ehdr->e_shnum; i++)
+ {
+ if (SHT_ARM_ATTRIBUTES == shdrs[i].sh_type)
+ {
+ /* We've found a likely section. Load the contents and
+ * check the tags */
+ unsigned char *contents = alloca(
+ unsigned char *p = contents;
+ unsigned char * end;
+
+ __lseek (fd, shdrs[i].sh_offset, SEEK_SET);
+ if ((size_t) __libc_read (fd, (void *) contents, shdrs[i].sh_size) != shdrs[i].sh_size)
+ return -1;
+
+ /* Sanity-check the attribute section details. Make sure
+ * that it's the "aeabi" section, that's all we care
+ * about. */
+ if (*p == 'A')
+ {
+ unsigned long len = shdrs[i].sh_size - 1;
+ unsigned long namelen;
+ p++;
+
+ while (len > 0)
+ {
+ unsigned long section_len = p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
+ if (section_len > len)
+ {
+ _dl_debug_printf_c (" invalid section len %lu, max remaining %lu\n", section_len, len);
+ section_len = len;
+ }
+
+ p += 4;
+ len -= section_len;
+ section_len -= 4;
+
+ if (0 != strcmp((char *)p, "aeabi"))
+ {
+ _dl_debug_printf_c (" ignoring unknown att...