Bug 14843

Summary: Can't audit IFUNC resolver functions
Product: glibc Reporter: H.J. Lu <hjl.tools>
Component: dynamic-linkAssignee: Not yet assigned to anyone <unassigned>
Status: NEW ---    
Severity: normal CC: amonakov, hjl.tools
Priority: P2 Flags: fweimer: security-
Version: 2.17   
Target Milestone: ---   
Host: Target:
Build: Last reconfirmed:
Bug Depends on: 14831    
Bug Blocks:    

Description H.J. Lu 2012-11-14 23:31:22 UTC
+++ This bug was initially created as a clone of Bug #14831 +++

Using the audit mechanism to redirect library lookups by implementing la_objsearch and returning a library that depends on libm.so (or libm.so itself) results in a subsequent segfault in the loader.  I have attempted to create a standalone testcase, but did not succeed (I suspect the bug has to do with how IRELATIVE relocations are processed, but a simple testcase with IRELATIVE reloc works fine).  Attaching a small testcase that depends on libm.so (and assumes it has IRELATIVE relocations).

$ gdb --args /tmp/glibc-build/elf/ld.so --audit ./libaudit.so ./main
GNU gdb (GDB) 7.4.1

(gdb) r
Starting program: /tmp/glibc-build/elf/ld.so --audit ./libaudit.so ./main
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?

Program received signal SIGSEGV, Segmentation fault.
_dl_profile_fixup (l=0x7ffff7a33508, reloc_arg=4, retaddr=140737345060825, regs=0x7fffffffd1b0, framesizep=0x7fffffffd508) at ../elf/dl-runtime.c:176
176	  DL_FIXUP_VALUE_TYPE value = *resultp;
(gdb) bt
#0  _dl_profile_fixup (l=0x7ffff7a33508, reloc_arg=4, retaddr=140737345060825, regs=0x7fffffffd1b0, framesizep=0x7fffffffd508) at ../elf/dl-runtime.c:176
#1  0x0000555555568306 in _dl_runtime_profile () at ../sysdeps/x86_64/dl-trampoline.h:48
#2  0x00007ffff7757fd9 in ?? ()
#3  0x00007fffffffd650 in ?? ()
#4  0x000055555555f5d1 in elf_machine_lazy_rel (skip_ifunc=<optimized out>, reloc=0x7ffff773e210, l_addr=140737344933888, map=0x7ffff7a33508) at ../sysdeps/x86_64/dl-machine.h:535
#5  elf_dynamic_do_Rela (skip_ifunc=<optimized out>, lazy=<optimized out>, nrelative=<optimized out>, relsize=<optimized out>, reladdr=<optimized out>, map=0x7ffff7a33508) at do-rel.h:85
#6  _dl_relocate_object (scope=0x7ffff7a33860, reloc_mode=<optimized out>, consider_profiling=1, consider_profiling@entry=0) at dl-reloc.c:265
#7  0x0000555555557ad2 in dl_main (phdr=<optimized out>, phdr@entry=0x555555554040, phnum=4160734848, phnum@entry=7, user_entry=user_entry@entry=0x7fffffffd7d8, auxv=0x555555777801) at rtld.c:2299
#8  0x0000555555568afc in _dl_sysdep_start (start_argptr=start_argptr@entry=0x7fffffffd890, dl_main=dl_main@entry=0x555555555ae0 <dl_main>) at ../elf/dl-sysdep.c:242
#9  0x0000555555558d0e in _dl_start_final (arg=0x7fffffffd890) at rtld.c:337
#10 _dl_start (arg=0x7fffffffd890) at rtld.c:563
#11 0x00005555555555a8 in _start () from /tmp/glibc-build/elf/ld.so
(gdb) p l.l_reloc_result 
$1 = (struct reloc_result *) 0x0
(gdb) f 4
#4  0x000055555555f5d1 in elf_machine_lazy_rel (skip_ifunc=<optimized out>, reloc=0x7ffff773e210, l_addr=140737344933888, map=0x7ffff7a33508) at ../sysdeps/x86_64/dl-machine.h:535
535		value = ((ElfW(Addr) (*) (void)) value) ();
(gdb) list
530	    }
531	  else if (__builtin_expect (r_type == R_X86_64_IRELATIVE, 0))
532	    {
533	      ElfW(Addr) value = map->l_addr + reloc->r_addend;
534	      if (__builtin_expect (!skip_ifunc, 1))
535		value = ((ElfW(Addr) (*) (void)) value) ();
536	      *reloc_addr = value;
537	    }
538	  else
539	    _dl_reloc_bad_type (map, r_type, 1);
(gdb)

This patch fixes the crash

---
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index e6968a4..e3c8b26 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -262,8 +262,6 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
 
 #include "dynamic-link.h"
 
-    ELF_DYNAMIC_RELOCATE (l, lazy, consider_profiling, skip_ifunc);
-
 #ifndef PROF
     if (__builtin_expect (consider_profiling, 0))
       {
@@ -290,6 +288,8 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
 	  }
       }
 #endif
+
+    ELF_DYNAMIC_RELOCATE (l, lazy, consider_profiling, skip_ifunc);
   }
 
   /* Mark the object so we know this work has been done.  */
---

However it causes tst-audit2 failure:

{abcdef72, d8675309} != {d8675309, abcdef72}

This test is added from:

http://www.sourceware.org/ml/libc-alpha/2005-09/msg00075.html
http://www.sourceware.org/ml/libc-alpha/2006-03/msg00138.html

From ELF_DYNAMIC_RELOCATE we call elf_machine_rela, which calls
CHECK_STATIC_TLS, which eventually allocates the static TLS *before*
the first call to any malloc-esque hooks.  That means we can't
audit IFUNC resolver functions because of this ordering issue.