[Bug 1821677] Re: dl_open segment fault in ubuntu18.10 glibc2.28

Sunil Pandey 1821677 at bugs.launchpad.net
Fri Mar 29 05:23:23 UTC 2019


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
+++ b/elf/dl-load.c
@@ -1438,6 +1438,209 @@
     _dl_debug_printf_c ("\t\t(%s)\n", what);
 }

+#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
+#define ABI_VFP_args          28
+#define VFP_ARGS_IN_VFP_REGS   1
+
+/* 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_arm_attributes_hfabi(int fd, ElfW(Ehdr) *ehdr, bool *is_hf)
+{
+  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(shdrs[i].sh_size);
+         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 attr section %s\n", p);
+                     p += section_len;
+                     continue;
+                    }
+                 namelen = strlen((char *)p) + 1;
+                 p += namelen;
+                 section_len -= namelen;
+
+                 /* We're in a valid section. Walk through this
+                  * section looking for the tag we care about
+                  * (ABI_VFP_args) */
+                 while (section_len > 0)
+                    {
+                     unsigned long val = 0;
+                     unsigned long tag;
+                     unsigned long size;
+
+                     end = p;
+                     tag = (*p++);
+
+                     size = p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
+                     if (size > section_len)
+                        {
+                         _dl_debug_printf_c ("    invalid subsection length %lu, max allowed %lu\n", size, section_len);
+                         size = section_len;
+                        }
+                     p += 4;
+
+                     section_len -= size;
+                     end += size;
+                     if (ATTR_TAG_FILE != tag)
+                        {
+                         /* ignore, we don't care */
+                         _dl_debug_printf_c ("    ignoring unknown subsection with type %lu length %lu\n", tag, size);
+                         p = end;
+                         continue;
+                        }
+                     while (p < end)
+                        {
+                         p = read_uleb128 (p, &tag);
+                         /* Handle the different types of tag. */
+                         if ( (tag == 4) || (tag == 5) || (tag == 67) )
+                            {
+                             /* Special cases for string values */
+                             namelen = strlen((char *)p) + 1;
+                             p += namelen;
+                            }
+                         else
+                            {
+                             p = read_uleb128 (p, &val);
+                            }
+                         if ( (tag == ABI_VFP_args) && (val == VFP_ARGS_IN_VFP_REGS) )
+                            {
+                             *is_hf = 1;
+                             return 0;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+  return 0;
+}
+
+

+/* ARM-specific checks. If we're built using the HF ABI, then fail any
+   attempts to use the SF ABI (and vice versa). Then, check for
+   consistency of ABI in terms of passing VFP args. */
+static int
+arm_specific_checks(int fd, const char *name, ElfW(Ehdr) *ehdr)
+{
+  static int all_hf = -1; /* unset */
+  bool is_hf = false;
+  int ret;
+
+  ret = check_arm_attributes_hfabi(fd, ehdr, &is_hf);
+  if (ret != 0)
+    return ret;
+
+#ifdef __ARM_PCS_VFP
+  if (!is_hf)
+    return EINVAL;
+#else
+  if (is_hf)
+    return EINVAL;
+#endif
+
+  if (all_hf == -1)
+    {
+      if (is_hf)
+       all_hf = 1;
+      else
+       all_hf = 0;
+    }
+  else if (all_hf == 1 && !is_hf)
+    return EINVAL;
+  else if (all_hf == 0 && is_hf)
+    return EINVAL;
+  return 0;
+}
+#endif
+
+

+/* Run any architecture-specific checks that might be needed for the
+   current architecture. */
+static int
+arch_specific_checks(int fd, const char *name, ElfW(Ehdr) *ehdr)
+{
+#ifdef __arm__
+    return arm_specific_checks(fd, name, ehdr);
+#endif
+
+  return 0;
+}
+
+

 /* Open a file and verify it is an ELF file for this architecture.  We
    ignore only ELF files for other architectures.  Non-ELF files and
    ELF files with different header information cause fatal errors since
@@ -1676,6 +1879,7 @@

       /* Check .note.ABI-tag if present.  */
       for (ph = phdr; ph < &phdr[ehdr->e_phnum]; ++ph)
+      {
        if (ph->p_type == PT_NOTE && ph->p_filesz >= 32 && ph->p_align >= 4)
          {
            ElfW(Addr) size = ph->p_filesz;
@@ -1751,6 +1955,21 @@
          }
       free (abi_note_malloced);
     }
+      if (-1 != fd)
+       {
+         int error = arch_specific_checks(fd, name, ehdr);
+         if (EINVAL == error)
+           {
+             goto close_and_out;
+           }
+         if (0 != error)
+           {
+             errstring = N_("Unable to run arch-specific checks\n");
+             goto call_lose;
+           }
+       }
+
+    }

   return fd;
 }

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

Title:
  dl_open segment fault in ubuntu18.10 glibc2.28

Status in glibc package in Ubuntu:
  New

Bug description:
  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("\nERROR: %s", dlerror());
      return -1;
    }
    if(dlclose(liball)==0) {printf("\n all ok\n");}
    return 0;
  }

  compile with 
  ~/work/glibc$ gcc -O0 -g foo.c -ldl

  then get segment fault:

  ~/work/glibc$ ./a.out intel64_lin/libsvml.so 
  Trying to open intel64_lin/libsvml.so
  Segmentation fault (core dumped)

  coredump as:

  (gdb) bt
  #0  __GI___libc_free (mem=0x7ffff7d49010) at malloc.c:3085
  #1  0x00007ffff7fdb6b6 in open_verify (
      name=0x555555559670 "/home/lilicui/intel64_lin/libsvml.so", 
      fbp=fbp at entry=0x7fffffffd530, loader=<optimized out>, 
      mode=mode at entry=-1879048190, 
      found_other_class=found_other_class at entry=0x7fffffffd51f, free_name=true, 
      whatcode=0, fd=3) at dl-load.c:1977
  #2  0x00007ffff7fdc926 in _dl_map_object (loader=loader at entry=0x7ffff7ffe190, 
      name=name at entry=0x7fffffffe1b7 "/home/lilicui/intel64_lin/libsvml.so", 
      type=type at entry=2, trace_mode=trace_mode at entry=0, 
      mode=mode at entry=-1879048190, nsid=<optimized out>) at dl-load.c:2401
  #3  0x00007ffff7fe79c4 in dl_open_worker (a=a at entry=0x7fffffffdaa0)
      at dl-open.c:228
  #4  0x00007ffff7f1b48f in __GI__dl_catch_exception (exception=<optimized out>, 
      operate=<optimized out>, args=<optimized out>) at dl-error-skeleton.c:196
  #5  0x00007ffff7fe72c6 in _dl_open (
      file=0x7fffffffe1b7 "/home/lilicui/intel64_lin/libsvml.so", 
      mode=-2147483646, caller_dlopen=0x5555555551cb <main+86>, 
      nsid=<optimized out>, argc=2, argv=0x7fffffffde08, env=0x7fffffffde20)
      at dl-open.c:599
  #6  0x00007ffff7faa256 in dlopen_doit (a=a at entry=0x7fffffffdcc0) at dlopen.c:66
  #7  0x00007ffff7f1b48f in __GI__dl_catch_exception (
      exception=exception at entry=0x7fffffffdc60, operate=<optimized out>, 
  --Type <RET> for more, q to quit, c to continue without paging--
      args=<optimized out>) at dl-error-skeleton.c:196
  #8  0x00007ffff7f1b51f in __GI__dl_catch_error (
      objname=0x7ffff7fae0f0 <last_result+16>, 
      errstring=0x7ffff7fae0f8 <last_result+24>, 
      mallocedp=0x7ffff7fae0e8 <last_result+8>, operate=<optimized out>, 
      args=<optimized out>) at dl-error-skeleton.c:215
  #9  0x00007ffff7faaa25 in _dlerror_run (
      operate=operate at entry=0x7ffff7faa200 <dlopen_doit>, 
      args=args at entry=0x7fffffffdcc0) at dlerror.c:163
  #10 0x00007ffff7faa2e6 in __dlopen (file=<optimized out>, mode=<optimized out>)
      at dlopen.c:87
  #11 0x00005555555551cb in main (argc=2, argv=0x7fffffffde08) at foo.c:7

  
  intel64_lin/libsvml.so is icc19.0(aleady released) runtime library, refer to attachment.

  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?

To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/glibc/+bug/1821677/+subscriptions



More information about the foundations-bugs mailing list