Bug 14831 - [Regression] Segfault in _dl_profile_fixup with IRELATIVE and LD_AUDIT
Summary: [Regression] Segfault in _dl_profile_fixup with IRELATIVE and LD_AUDIT
Status: RESOLVED FIXED
Alias: None
Product: glibc
Classification: Unclassified
Component: dynamic-link (show other bugs)
Version: 2.16
: P2 normal
Target Milestone: 2.16
Assignee: Not yet assigned to anyone
URL:
Keywords:
: 13818 (view as bug list)
Depends on:
Blocks: 14843
  Show dependency treegraph
 
Reported: 2012-11-11 21:03 UTC by Alexander Monakov
Modified: 2014-06-14 11:08 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:
fweimer: security-


Attachments
testcase (653 bytes, application/x-bzip2)
2012-11-11 21:03 UTC, Alexander Monakov
Details
simplified testcase (367 bytes, application/x-bzip2)
2012-11-12 07:27 UTC, Alexander Monakov
Details
standalone testcase (545 bytes, application/x-bzip2)
2012-11-13 19:10 UTC, Alexander Monakov
Details
A testcase (481 bytes, patch)
2012-11-13 20:35 UTC, H.J. Lu
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Alexander Monakov 2012-11-11 21:03:48 UTC
Created attachment 6727 [details]
testcase

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)
Comment 1 Alexander Monakov 2012-11-12 07:27:33 UTC
Created attachment 6728 [details]
simplified testcase

Actually, redirecting libraries via LD_AUDIT mechanism is not required to trigger the bug. It suffices to use the minimal audit library that implements only la_version and an executable that links to libm.so.

I'm surprised this simplified test fails. Is it covered in glibc's LD_AUDIT tests? Does it point to an inefficiency somewhere (LD_AUDIT library is essentially a no-op in this case, it's odd that the loader's behavior differs)?
Comment 2 Alexander Monakov 2012-11-13 19:10:13 UTC
Created attachment 6732 [details]
standalone testcase

Attached a standalone testcase that demonstrates the issue.

This bug is a regression that prevents using LD_AUDIT with any executables that link against libm.so on some of the recent Linux distributions (and most of g++-compiled applications link with libm).

I'm not familiar with the rtld guts at all, but here's my understanding of what happens:

1. elf_machine_lazy_rel calls the ifunc resolver
2. the resolver calls another function in the dso
3. the plt entry(?) has been instrumented for dso profiling, and calls _dl_profile_fixup.
4. _dl_profile_fixup accesses l->l_reloc_result, which is NULL and will be allocated later in _dl_relocate_object

It seems to me that instrumenting for profiling is not necessary if the audit library does not export la_plt* routines. Thus, I think there are two separate issues here.
Comment 3 H.J. Lu 2012-11-13 19:31:28 UTC
There are several audit tests:

elf/tst-audit1.c  elf/tst-auditmod1.c   elf/tst-auditmod6a.c
elf/tst-audit2.c  elf/tst-auditmod3a.c  elf/tst-auditmod6b.c
elf/tst-audit3.c  elf/tst-auditmod3b.c  elf/tst-auditmod6c.c
elf/tst-audit4.c  elf/tst-auditmod4a.c  elf/tst-auditmod7a.c
elf/tst-audit5.c  elf/tst-auditmod4b.c  elf/tst-auditmod7b.c
elf/tst-audit6.c  elf/tst-auditmod5a.c
elf/tst-audit7.c  elf/tst-auditmod5b.c

Do they fail for you?
Comment 4 Alexander Monakov 2012-11-13 20:01:16 UTC
They pass.
Comment 5 H.J. Lu 2012-11-13 20:18:07 UTC
(In reply to comment #4)
> They pass.

Those tests are much more complex. Can add your testcase to elf
directory and make it to fail?
Comment 6 Alexander Monakov 2012-11-13 20:33:33 UTC
Those tests do not exercise IFUNC at all. I could make a test suitable for the glibc test harness, but I already spent enough effort on producing a standalone test case that clearly demonstrates the issue in isolation. Have you tried my standalone testcase?
Comment 7 H.J. Lu 2012-11-13 20:35:52 UTC
Created attachment 6733 [details]
A testcase
Comment 8 H.J. Lu 2012-11-13 22:37:41 UTC
A patch is posted at

http://sourceware.org/ml/libc-alpha/2012-11/msg00334.html
Comment 9 H.J. Lu 2012-11-14 23:52:16 UTC
Fixed on trunk by

http://sourceware.org/git/?p=glibc.git;a=commit;h=2e64d2659d3edaebc792ac596a9863f1626e5c25

Need m68k/sh ELF_MACHINE_RUNTIME_FIXUP_PARAMS.
Comment 10 Thomas Schwinge 2012-11-20 18:41:21 UTC
(In reply to comment #9)
> Need m68k/sh ELF_MACHINE_RUNTIME_FIXUP_PARAMS.

Completed in commit e510ab5efff3450b723dbe71734e8b22be14d1c6 (m68k), and
commit d072f3f7724d85ceaf230806660235f0cf2f9c3b (SH).
Comment 11 H.J. Lu 2012-11-27 16:50:48 UTC
Fixed on 2.16 branch.
Comment 12 Alexander Monakov 2013-05-27 09:26:11 UTC
*** Bug 13818 has been marked as a duplicate of this bug. ***