[BZ #15022] Correct global-scope dlopen issues in static executables.

This change creates a link map in static executables to serve as the
global search list for dlopen.  It fixes a problem with the inability
to access the global symbol object and a crash on an attempt to map a
DSO into the global scope.  Some code that has become dead after the
addition of this link map is removed too and test cases are provided.
This commit is contained in:
Maciej W. Rozycki 2013-06-28 16:20:26 +01:00
parent ed0257f7d3
commit f91f1c0fb8
9 changed files with 610 additions and 42 deletions

View File

@ -1,3 +1,26 @@
2013-06-28 Maciej W. Rozycki <macro@codesourcery.com>
[BZ #15022]
* elf/dl-support.c (_dl_main_map): New variable.
(_dl_ns): Use it to initialize [LM_ID_BASE] element.
(_dl_nns, _dl_load_adds): Set to 1.
(_dl_initial_searchlist): Refer to _dl_main_map.
(_dl_non_dynamic_init): Initialize _dl_main_map.l_origin.
* elf/dl-load.c (_dl_dst_substitute) [!SHARED]: Remove fallback
call to _dl_get_origin.
* elf/dl-open.c (dl_open_worker): Remove !SHARED special casing
around call_map.
(_dl_open) [!SHARED]: Remove code to initialize GL(dl_nns).
* dlfcn/modstatic3.c: New file.
* dlfcn/tststatic3.c: New file.
* dlfcn/tststatic4.c: New file.
* dlfcn/Makefile (tests): Add tststatic3 and tststatic4.
(tests-static): Likewise.
(modules-names): Add modstatic3.
(tststatic3-ENV, tststatic4-ENV): New variables.
($(objpfx)tststatic3, $(objpfx)tststatic3.out): New dependencies.
($(objpfx)tststatic4, $(objpfx)tststatic4.out): Likewise.
2013-06-26 Joseph Myers <joseph@codesourcery.com>
* configure.in (CC): Require GCC version 4.4 or later.

16
NEWS
View File

@ -14,14 +14,14 @@ Version 2.18
14176, 14200, 14256, 14280, 14293, 14317, 14327, 14478, 14496, 14582,
14686, 14812, 14888, 14894, 14907, 14908, 14909, 14920, 14952, 14964,
14981, 14982, 14985, 14991, 14994, 14996, 15000, 15003, 15006, 15007,
15014, 15020, 15023, 15036, 15054, 15055, 15062, 15078, 15084, 15085,
15086, 15100, 15160, 15214, 15221, 15232, 15234, 15283, 15285, 15287,
15304, 15305, 15307, 15309, 15327, 15330, 15335, 15336, 15337, 15339,
15342, 15346, 15359, 15361, 15366, 15380, 15381, 15394, 15395, 15405,
15406, 15409, 15416, 15418, 15419, 15423, 15424, 15426, 15429, 15431,
15432, 15441, 15442, 15448, 15465, 15480, 15485, 15488, 15490, 15492,
15493, 15497, 15506, 15529, 15536, 15553, 15577, 15583, 15618, 15627,
15631, 15654, 15655, 15666, 15667, 15674.
15014, 15020, 15022, 15023, 15036, 15054, 15055, 15062, 15078, 15084,
15085, 15086, 15100, 15160, 15214, 15221, 15232, 15234, 15283, 15285,
15287, 15304, 15305, 15307, 15309, 15327, 15330, 15335, 15336, 15337,
15339, 15342, 15346, 15359, 15361, 15366, 15380, 15381, 15394, 15395,
15405, 15406, 15409, 15416, 15418, 15419, 15423, 15424, 15426, 15429,
15431, 15432, 15441, 15442, 15448, 15465, 15480, 15485, 15488, 15490,
15492, 15493, 15497, 15506, 15529, 15536, 15553, 15577, 15583, 15618,
15627, 15631, 15654, 15655, 15666, 15667, 15674.
* CVE-2013-0242 Buffer overrun in regexp matcher has been fixed (Bugzilla
#15078).

View File

@ -47,11 +47,13 @@ glreflib2.so-no-z-defs = yes
errmsg1mod.so-no-z-defs = yes
ifeq (yes,$(build-shared))
tests += tststatic tststatic2
tests-static += tststatic tststatic2
modules-names += modstatic modstatic2
tests += tststatic tststatic2 tststatic3 tststatic4
tests-static += tststatic tststatic2 tststatic3 tststatic4
modules-names += modstatic modstatic2 modstatic3
tststatic-ENV = LD_LIBRARY_PATH=$(objpfx):$(common-objpfx):$(common-objpfx)elf
tststatic2-ENV = $(tststatic-ENV)
tststatic3-ENV = $(tststatic-ENV)
tststatic4-ENV = $(tststatic-ENV)
endif
extra-test-objs += $(modules-names:=.os)
@ -104,6 +106,12 @@ $(objpfx)tststatic2.out: $(objpfx)tststatic2 $(objpfx)modstatic.so \
$(objpfx)modstatic2.so: $(libdl)
$(objpfx)tststatic3: $(objpfx)libdl.a
$(objpfx)tststatic3.out: $(objpfx)tststatic3 $(objpfx)modstatic3.so
$(objpfx)tststatic4: $(objpfx)libdl.a
$(objpfx)tststatic4.out: $(objpfx)tststatic4 $(objpfx)modstatic3.so
$(objpfx)bug-dlopen1: $(libdl)
$(objpfx)bug-dlsym1: $(libdl) $(objpfx)bug-dlsym1-lib2.so

31
dlfcn/modstatic3.c Normal file
View File

@ -0,0 +1,31 @@
/* DSO used for dlopen testing with a static executable.
Copyright (C) 2013 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
unsigned int foo;
unsigned int
getfoo (void)
{
return foo;
}
void
setfoo (unsigned int f)
{
foo = f;
}

128
dlfcn/tststatic3.c Normal file
View File

@ -0,0 +1,128 @@
/* Global-scope DSO mapping test with a static executable (BZ #15022).
Copyright (C) 2013 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <dlfcn.h>
#include <stddef.h>
#include <stdio.h>
#define MAGIC0 0
#define MAGIC1 0x5500ffaa
#define MAGIC2 0xaaff0055
/* Mapping a DSO into the global scope used to crash in static
executables. Check that it succeeds and then that symbols from
the DSO can be accessed and operate as expected. */
static int
do_test (void)
{
unsigned int (*getfoo) (void);
void (*setfoo) (unsigned int);
unsigned int *foop;
unsigned int foo;
void *handle;
/* Try to map a module into the global scope. */
handle = dlopen ("modstatic3.so", RTLD_LAZY | RTLD_GLOBAL);
if (handle == NULL)
{
printf ("dlopen (modstatic3.so): %s\n", dlerror ());
return 1;
}
/* Get at its symbols. */
foop = dlsym (handle, "foo");
if (foop == NULL)
{
printf ("dlsym (foo): %s\n", dlerror ());
return 1;
}
getfoo = dlsym (handle, "getfoo");
if (getfoo == NULL)
{
printf ("dlsym (getfoo): %s\n", dlerror ());
return 1;
}
setfoo = dlsym (handle, "setfoo");
if (setfoo == NULL)
{
printf ("dlsym (setfoo): %s\n", dlerror ());
return 1;
}
/* Make sure the view of the initial state is consistent. */
foo = *foop;
if (foo != MAGIC0)
{
printf ("*foop: got %#x, expected %#x\n", foo, MAGIC0);
return 1;
}
foo = getfoo ();
if (foo != MAGIC0)
{
printf ("getfoo: got %#x, expected %#x\n", foo, MAGIC0);
return 1;
}
/* Likewise with one change to its state. */
setfoo (MAGIC1);
foo = *foop;
if (foo != MAGIC1)
{
printf ("*foop: got %#x, expected %#x\n", foo, MAGIC1);
return 1;
}
foo = getfoo ();
if (foo != MAGIC1)
{
printf ("getfoo: got %#x, expected %#x\n", foo, MAGIC1);
return 1;
}
/* And with another. */
setfoo (MAGIC2);
foo = *foop;
if (foo != MAGIC2)
{
printf ("*foop: got %#x, expected %#x\n", foo, MAGIC2);
return 1;
}
foo = getfoo ();
if (foo != MAGIC2)
{
printf ("getfoo: got %#x, expected %#x\n", foo, MAGIC2);
return 1;
}
/* All done, clean up. */
getfoo = NULL;
setfoo = NULL;
foop = NULL;
dlclose (handle);
return 0;
}
#define TEST_FUNCTION do_test ()
#include "../test-skeleton.c"

363
dlfcn/tststatic4.c Normal file
View File

@ -0,0 +1,363 @@
/* Global object symbol access tests with a static executable (BZ #15022).
Copyright (C) 2013 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <dlfcn.h>
#include <stddef.h>
#include <stdio.h>
#define MAGIC0 0
#define MAGIC1 0x5500ffaa
#define MAGIC2 0xaaff0055
#define MAGIC3 0xff55aa00
/* Check the ability to access the global symbol object and then
global-scope symbol access consistency via different mappings
requested from a static executable. */
static int
do_test (void)
{
unsigned int (*initial_getfoo) (void);
void (*initial_setfoo) (unsigned int);
unsigned int (*global_getfoo) (void);
void (*global_setfoo) (unsigned int);
unsigned int (*local_getfoo) (void);
void (*local_setfoo) (unsigned int);
unsigned int *initial_foop;
unsigned int *global_foop;
unsigned int *local_foop;
void *initial_handle;
void *global_handle;
void *local_handle;
unsigned int foo;
/* Try to map self. */
initial_handle = dlopen (NULL, RTLD_LAZY | RTLD_GLOBAL);
if (initial_handle == NULL)
{
printf ("dlopen [initial] (NULL): %s\n", dlerror ());
return 1;
}
/* Make sure symbol lookups fail gracefully. */
initial_foop = dlsym (initial_handle, "foo");
if (initial_foop != NULL)
{
printf ("dlsym [initial] (foo): got %p, expected NULL\n", initial_foop);
return 1;
}
initial_getfoo = dlsym (initial_handle, "getfoo");
if (initial_getfoo != NULL)
{
printf ("dlsym [initial] (getfoo): got %p, expected NULL\n",
initial_getfoo);
return 1;
}
initial_setfoo = dlsym (initial_handle, "setfoo");
if (initial_setfoo != NULL)
{
printf ("dlsym [initial] (setfoo): got %p, expected NULL\n",
initial_setfoo);
return 1;
}
/* Try to map a module into the global scope. */
global_handle = dlopen ("modstatic3.so", RTLD_LAZY | RTLD_GLOBAL);
if (global_handle == NULL)
{
printf ("dlopen [global] (modstatic3.so): %s\n", dlerror ());
return 1;
}
/* Get at its symbols. */
global_foop = dlsym (global_handle, "foo");
if (global_foop == NULL)
{
printf ("dlsym [global] (foo): %s\n", dlerror ());
return 1;
}
global_getfoo = dlsym (global_handle, "getfoo");
if (global_getfoo == NULL)
{
printf ("dlsym [global] (getfoo): %s\n", dlerror ());
return 1;
}
global_setfoo = dlsym (global_handle, "setfoo");
if (global_setfoo == NULL)
{
printf ("dlsym [global] (setfoo): %s\n", dlerror ());
return 1;
}
/* Try to map self again now. */
local_handle = dlopen (NULL, RTLD_LAZY | RTLD_LOCAL);
if (local_handle == NULL)
{
printf ("dlopen [local] (NULL): %s\n", dlerror ());
return 1;
}
/* Make sure we can get at the previously loaded module's symbols
via this handle too. */
local_foop = dlsym (local_handle, "foo");
if (local_foop == NULL)
{
printf ("dlsym [local] (foo): %s\n", dlerror ());
return 1;
}
local_getfoo = dlsym (local_handle, "getfoo");
if (local_getfoo == NULL)
{
printf ("dlsym [local] (getfoo): %s\n", dlerror ());
return 1;
}
local_setfoo = dlsym (local_handle, "setfoo");
if (local_setfoo == NULL)
{
printf ("dlsym [local] (setfoo): %s\n", dlerror ());
return 1;
}
/* Make sure we can get at the previously loaded module's symbols
via a handle that was obtained before the module was loaded too. */
initial_foop = dlsym (initial_handle, "foo");
if (initial_foop == NULL)
{
printf ("dlsym [initial] (foo): %s\n", dlerror ());
return 1;
}
initial_getfoo = dlsym (initial_handle, "getfoo");
if (initial_getfoo == NULL)
{
printf ("dlsym [initial] (getfoo): %s\n", dlerror ());
return 1;
}
initial_setfoo = dlsym (initial_handle, "setfoo");
if (initial_setfoo == NULL)
{
printf ("dlsym [initial] (setfoo): %s\n", dlerror ());
return 1;
}
/* Make sure the view of the initial state is consistent. */
foo = *initial_foop;
if (foo != MAGIC0)
{
printf ("*foop [initial]: got %#x, expected %#x\n", foo, MAGIC0);
return 1;
}
foo = *global_foop;
if (foo != MAGIC0)
{
printf ("*foop [global]: got %#x, expected %#x\n", foo, MAGIC0);
return 1;
}
foo = *local_foop;
if (foo != MAGIC0)
{
printf ("*foop [local]: got %#x, expected %#x\n", foo, MAGIC0);
return 1;
}
foo = initial_getfoo ();
if (foo != MAGIC0)
{
printf ("getfoo [initial]: got %#x, expected %#x\n", foo, MAGIC0);
return 1;
}
foo = global_getfoo ();
if (foo != MAGIC0)
{
printf ("getfoo [global]: got %#x, expected %#x\n", foo, MAGIC0);
return 1;
}
foo = local_getfoo ();
if (foo != MAGIC0)
{
printf ("getfoo [local]: got %#x, expected %#x\n", foo, MAGIC0);
return 1;
}
/* Likewise with a change to its state made through the first handle. */
initial_setfoo (MAGIC1);
foo = *initial_foop;
if (foo != MAGIC1)
{
printf ("*foop [initial]: got %#x, expected %#x\n", foo, MAGIC1);
return 1;
}
foo = *global_foop;
if (foo != MAGIC1)
{
printf ("*foop [global]: got %#x, expected %#x\n", foo, MAGIC1);
return 1;
}
foo = *local_foop;
if (foo != MAGIC1)
{
printf ("*foop [local]: got %#x, expected %#x\n", foo, MAGIC1);
return 1;
}
foo = initial_getfoo ();
if (foo != MAGIC1)
{
printf ("getfoo [initial]: got %#x, expected %#x\n", foo, MAGIC1);
return 1;
}
foo = global_getfoo ();
if (foo != MAGIC1)
{
printf ("getfoo [global]: got %#x, expected %#x\n", foo, MAGIC1);
return 1;
}
foo = local_getfoo ();
if (foo != MAGIC1)
{
printf ("getfoo [local]: got %#x, expected %#x\n", foo, MAGIC1);
return 1;
}
/* Likewise with a change to its state made through the second handle. */
global_setfoo (MAGIC2);
foo = *initial_foop;
if (foo != MAGIC2)
{
printf ("*foop [initial]: got %#x, expected %#x\n", foo, MAGIC2);
return 1;
}
foo = *global_foop;
if (foo != MAGIC2)
{
printf ("*foop [global]: got %#x, expected %#x\n", foo, MAGIC2);
return 1;
}
foo = *local_foop;
if (foo != MAGIC2)
{
printf ("*foop [local]: got %#x, expected %#x\n", foo, MAGIC2);
return 1;
}
foo = initial_getfoo ();
if (foo != MAGIC2)
{
printf ("getfoo [initial]: got %#x, expected %#x\n", foo, MAGIC2);
return 1;
}
foo = global_getfoo ();
if (foo != MAGIC2)
{
printf ("getfoo [global]: got %#x, expected %#x\n", foo, MAGIC2);
return 1;
}
foo = local_getfoo ();
if (foo != MAGIC2)
{
printf ("getfoo [local]: got %#x, expected %#x\n", foo, MAGIC2);
return 1;
}
/* Likewise with a change to its state made through the third handle. */
local_setfoo (MAGIC3);
foo = *initial_foop;
if (foo != MAGIC3)
{
printf ("*foop [initial]: got %#x, expected %#x\n", foo, MAGIC3);
return 1;
}
foo = *global_foop;
if (foo != MAGIC3)
{
printf ("*foop [global]: got %#x, expected %#x\n", foo, MAGIC3);
return 1;
}
foo = *local_foop;
if (foo != MAGIC3)
{
printf ("*foop [local]: got %#x, expected %#x\n", foo, MAGIC3);
return 1;
}
foo = initial_getfoo ();
if (foo != MAGIC3)
{
printf ("getfoo [initial]: got %#x, expected %#x\n", foo, MAGIC3);
return 1;
}
foo = global_getfoo ();
if (foo != MAGIC3)
{
printf ("getfoo [global]: got %#x, expected %#x\n", foo, MAGIC3);
return 1;
}
foo = local_getfoo ();
if (foo != MAGIC3)
{
printf ("getfoo [local]: got %#x, expected %#x\n", foo, MAGIC3);
return 1;
}
/* All done, clean up. */
initial_getfoo = NULL;
initial_setfoo = NULL;
initial_foop = NULL;
local_getfoo = NULL;
local_setfoo = NULL;
local_foop = NULL;
dlclose (local_handle);
global_getfoo = NULL;
global_setfoo = NULL;
global_foop = NULL;
dlclose (global_handle);
dlclose (initial_handle);
return 0;
}
#define TEST_FUNCTION do_test ()
#include "../test-skeleton.c"

View File

@ -342,13 +342,7 @@ _dl_dst_substitute (struct link_map *l, const char *name, char *result,
if ((len = is_dst (start, name, "ORIGIN", is_path,
INTUSE(__libc_enable_secure))) != 0)
{
#ifndef SHARED
if (l == NULL)
repl = _dl_get_origin ();
else
#endif
repl = l->l_origin;
repl = l->l_origin;
check_for_trusted = (INTUSE(__libc_enable_secure)
&& l->l_type == lt_executable);
}

View File

@ -204,11 +204,9 @@ dl_open_worker (void *a)
{
const void *caller_dlopen = args->caller_dlopen;
#ifdef SHARED
/* We have to find out from which object the caller is calling.
By default we assume this is the main application. */
call_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
#endif
struct link_map *l = _dl_find_dso_for_object ((ElfW(Addr)) caller_dlopen);
@ -216,15 +214,7 @@ dl_open_worker (void *a)
call_map = l;
if (args->nsid == __LM_ID_CALLER)
{
#ifndef SHARED
/* In statically linked apps there might be no loaded object. */
if (call_map == NULL)
args->nsid = LM_ID_BASE;
else
#endif
args->nsid = call_map->l_ns;
}
args->nsid = call_map->l_ns;
}
assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT);
@ -642,12 +632,6 @@ no more namespaces available for dlmopen()"));
|| GL(dl_ns)[nsid]._ns_loaded->l_auditing))
_dl_signal_error (EINVAL, file, NULL,
N_("invalid target namespace in dlmopen()"));
#ifndef SHARED
else if ((nsid == LM_ID_BASE || nsid == __LM_ID_CALLER)
&& GL(dl_ns)[LM_ID_BASE]._ns_loaded == NULL
&& GL(dl_nns) == 0)
GL(dl_nns) = 1;
#endif
struct dl_open_args args;
args.file = file;

View File

@ -70,17 +70,52 @@ const char *_dl_origin_path;
/* Nonzero if runtime lookup should not update the .got/.plt. */
int _dl_bind_not;
/* A dummy link map for the executable, used by dlopen to access the global
scope. We don't export any symbols ourselves, so this can be minimal. */
static struct link_map _dl_main_map =
{
.l_name = (char *) "",
.l_real = &_dl_main_map,
.l_ns = LM_ID_BASE,
.l_libname = &(struct libname_list) { .name = "", .dont_free = 1 },
.l_searchlist =
{
.r_list = &(struct link_map *) { &_dl_main_map },
.r_nlist = 1,
},
.l_symbolic_searchlist = { .r_list = &(struct link_map *) { NULL } },
.l_type = lt_executable,
.l_scope_mem = { &_dl_main_map.l_searchlist },
.l_scope_max = (sizeof (_dl_main_map.l_scope_mem)
/ sizeof (_dl_main_map.l_scope_mem[0])),
.l_scope = _dl_main_map.l_scope_mem,
.l_local_scope = { &_dl_main_map.l_searchlist },
.l_used = 1,
.l_tls_offset = NO_TLS_OFFSET,
.l_serial = 1,
};
/* Namespace information. */
struct link_namespaces _dl_ns[DL_NNS];
size_t _dl_nns;
struct link_namespaces _dl_ns[DL_NNS] =
{
[LM_ID_BASE] =
{
._ns_loaded = &_dl_main_map,
._ns_nloaded = 1,
._ns_main_searchlist = &_dl_main_map.l_searchlist,
}
};
size_t _dl_nns = 1;
/* Incremented whenever something may have been added to dl_loaded. */
unsigned long long _dl_load_adds;
unsigned long long _dl_load_adds = 1;
/* Fake scope. In dynamically linked binaries this is the scope of the
main application but here we don't have something like this. So
create a fake scope containing nothing. */
struct r_scope_elem _dl_initial_searchlist;
/* Fake scope of the main application. */
struct r_scope_elem _dl_initial_searchlist =
{
.r_list = &(struct link_map *) { &_dl_main_map },
.r_nlist = 1,
};
#ifndef HAVE_INLINED_SYSCALLS
/* Nonzero during startup. */
@ -265,6 +300,8 @@ void
internal_function
_dl_non_dynamic_init (void)
{
_dl_main_map.l_origin = _dl_get_origin ();
if (HP_TIMING_AVAIL)
HP_TIMING_NOW (_dl_cpuclock_offset);