Bug 13823 - Bogus LD_AUDIT can cause target binary to segfault
Summary: Bogus LD_AUDIT can cause target binary to segfault
Status: NEW
Alias: None
Product: glibc
Classification: Unclassified
Component: dynamic-link (show other bugs)
Version: 2.15
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
Depends on:
Reported: 2012-03-08 16:54 UTC by law
Modified: 2017-06-07 07:31 UTC (History)
4 users (show)

See Also:
Last reconfirmed:
fweimer: security-


Note You need to log in before you can comment on or make changes to this bug.
Description law 2012-03-08 16:54:34 UTC
Specifying an invalid LD_AUDIT file can cause the target application to segfault.

First, you want the target program you're using to do something like setlocale
right after it starts.  /bin/true and /bin/false are good for this purpose.

Second, prelinking is necessary; I don't know why yet, but it was definitely
necessary to run a prelink -a after updating ld.so to trigger the behaviour.

Third, the auditing bits must be bogus, preferably a non-existent file.

So, something like

LD_AUDIT=/blah /bin/true

Where /blah does not exist turns out to be a good reproducer.

From what I've been able to put together, when LD_AUDIT is specified we call
init_tls earlier than normal:

 /* If we have auditing DSOs to load, do it now.  */
  if (__builtin_expect (audit_list != NULL, 0))
      /* Iterate over all entries in the list.  The order is important.  */
      struct audit_ifaces *last_audit = NULL;
      struct audit_list *al = audit_list->next;

      /* Since we start using the auditing DSOs right away we need to
         initialize the data structures now.  */
      tcbp = init_tls ();

 /* Load all the libraries specified by DT_NEEDED entries.  If LD_PRELOAD
     specified some libraries to load, these are inserted before the actual
     dependencies in the executable's searchlist for symbol resolution.  */
  HP_TIMING_NOW (start);
  _dl_map_object_deps (main_map, preloads, npreloads, mode == trace, 0);
  HP_TIMING_NOW (stop);
  HP_TIMING_DIFF (diff, start, stop);
  HP_TIMING_ACCUM_NT (load_time, diff);

 /* We do not initialize any of the TLS functionality unless any of the
     initial modules uses TLS.  This makes dynamic loading of modules with
     TLS impossible, but to support it requires either eagerly doing setup
     now or lazily doing it later.  Doing it now makes us incompatible with
     an old kernel that can't perform TLS_INIT_TP, even if no TLS is ever
     used.  Trying to do it lazily is too hairy to try when there could be
     multiple threads (from a non-TLS-using libpthread).  */
  bool was_tls_init_tp_called = tls_init_tp_called;
  if (tcbp == NULL)
    tcbp = init_tls ();

At the earlier init_tls call we haven't seen a DSO with TLS bits.  As a result
init_tls & eventually dl_allocate_tls_init have nothing interesting to do.  The
result being the TLS bits in libc.so.6 aren't initialized and all hell breaks
loose in the locale bits as the thread local variables aren't properly

Ideally if the auditing module is bogus we should just ignore it and the application should run normally.  Segfaulting is, umm, bad.

I pondered delaying the first init_tls call until we know the auditing module is loadable, but I'm concerned that's simply too late.
Comment 1 Gopobandhu Sahu 2012-09-10 14:52:37 UTC
I have found that it's not necessary that it needs to be a bogus LD_AUDIT library.
for latrace also it's failing while linking the second level libraries (i.e. dependent libraries of libraries)

You can reproduce this issue by running

> latrace gedit

the above command cause segfault in the application
Comment 2 Ondrej Bilka 2013-10-21 06:55:13 UTC
Looks almost as duplicate of 15199.
Comment 3 Carlos O'Donell 2017-06-07 01:19:54 UTC
(In reply to Ondrej Bilka from comment #2)
> Looks almost as duplicate of 15199.

I doubt it is a duplicate of 15199.

While working on rhel-5.11 / glibc 2.5 I ran into exactly the same issue as Jeff.

You can see that TLS is broken in the code accessing __ctype_b and that the register value is zero.

Florian Weimer and I found that this is somehow also related to prelink. If you undo prelinking then everything works.

It may be that with prelinking the dlmopen code links the same library twice into the same address range and the namespaces conflict?

We may never need to look at this again given that prelink isn't really being actively developed or used by newer distributions.

I won't close this out yet, but we should at some point just close this issue.
Comment 4 Florian Weimer 2017-06-07 07:31:03 UTC
(In reply to Carlos O'Donell from comment #3)
> Florian Weimer and I found that this is somehow also related to prelink. If
> you undo prelinking then everything works.

Jeff also noted that as a requirement to expose the bug, which is why I identified our issue as a duplicate.