Bug 23462 - Static binary with dynamic string tokens ($LIB, $PLATFORM, $ORIGIN) crashes
Summary: Static binary with dynamic string tokens ($LIB, $PLATFORM, $ORIGIN) crashes
Status: RESOLVED FIXED
Alias: None
Product: glibc
Classification: Unclassified
Component: dynamic-link (show other bugs)
Version: 2.29
: P2 normal
Target Milestone: 2.34
Assignee: Florian Weimer
URL:
Keywords:
: 27499 (view as bug list)
Depends on:
Blocks:
 
Reported: 2018-07-27 18:46 UTC by Carlos O'Donell
Modified: 2021-03-16 09:27 UTC (History)
4 users (show)

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


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Carlos O'Donell 2018-07-27 18:46:17 UTC
A static binary crashes when started with dynamic string tokens in LD_LIBRARY_PATH.

For example:

LD_LIBRARY_PATH=/foo/'$LIB' ./elf/ldconfig 
Segmentation fault (core dumped)

The problem is here:

elf/dl-load.c (_dl_init_paths):

 751 #ifdef SHARED
 752   /* This points to the map of the main object.  */
 753   l = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
 754   if (l != NULL)

We only load l within #ifdef SHARED, but later on we use it unconditionally in the following call chain fillin_rpath -> expand_dynamic_string_token -> DL_DST_REQUIRED.

Previously we had:

# define DL_DST_REQ_STATIC(l) \
  if ((l) == NULL)                                                            \
    {                                                                         \
      const char *origin = _dl_get_origin ();                                 \
      dst_len = (origin && origin != (char *) -1 ? strlen (origin) : 0);      \
    }                                                                         \
  else
#endif

Which guarded against this case, but after:

commit d1d5471579eb0426671bf94f2d71e61dfb204c30
Author: Maciej W. Rozycki <macro@codesourcery.com>
Date:   Sat Jun 22 00:39:42 2013 +0100

    Remove dead DL_DST_REQ_STATIC code.

We removed it.

Maciej's work was specifically designed to make static/dynamic cases have parity here, and so it just looks like an oversight that we don't unconditionally set the link map.

The solution is to always set the link map l, and remove the 'if ((l) == NULL)' because we are never NULL.

Interestingly coverity scan detected a logical problem here because it said if l is ever NULL then we'll crash, and it was right.

I have a patch for this and it passes the above test. I'll submit it shortly.
Comment 1 Carlos O'Donell 2019-10-08 14:04:20 UTC
OK, so for real I'm going to post this. I dropped the ball.
Comment 2 Christoph Höger 2020-09-18 10:42:20 UTC
This bug just hit me during a bootstrap of gcc, as the tls detection macro attempts a compilation with -static and is happy if compilation fails but unhappy if there is a segfault later on:

https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=config/tls.m4;h=7532305b908abf8c9874d466dc91eeed18954933;hb=HEAD#l12

The bug still persists in 2.32 - @Carlos
Comment 3 Florian Weimer 2021-03-03 03:34:04 UTC
*** Bug 27499 has been marked as a duplicate of this bug. ***
Comment 4 Carlos O'Donell 2021-03-03 14:08:40 UTC
We have an upstream patch for this, but the consequences are not entirely trivial, we have to consider what happens for static binaries. Thanks for Florian for initial review.

https://patchwork.sourceware.org/project/glibc/patch/ca723e73-c8eb-ca4c-1ca0-5fbf2b88edcd@redhat.com/
Comment 5 Florian Weimer 2021-03-05 15:08:33 UTC
I'm going to post an updated patch.
Comment 6 Florian Weimer 2021-03-12 15:45:52 UTC
Fixed for glibc 2.34 via:

commit 332421312576bd7095e70589154af99b124dd2d1
Author: Carlos O'Donell <carlos@redhat.com>
Date:   Fri Mar 12 16:44:47 2021 +0100

    elf: Always set l in _dl_init_paths (bug 23462)
    
    After d1d5471579eb0426671bf94f2d71e61dfb204c30 ("Remove dead
    DL_DST_REQ_STATIC code.") we always setup the link map l to make the
    static and shared cases the same.  The bug is that in elf/dl-load.c
    (_dl_init_paths) we conditionally set l only in the #ifdef SHARED
    case, but unconditionally use it later.  The simple solution is to
    remove the #ifdef SHARED conditional, because it's no longer needed,
    and unconditionally setup l for both the static and shared cases. A
    regression test is added to run a static binary with
    LD_LIBRARY_PATH='$ORIGIN' which crashes before the fix and runs after
    the fix.
    
    Co-Authored-By: Florian Weimer <fweimer@redhat.com>