From eec8b6cae586451deadf30c371f7b5e4c9d573d1 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Sat, 27 Mar 2004 03:40:33 +0000 Subject: Update. 2004-03-26 Ulrich Drepper * elf/dl-caller.c: New file. * include/caller.h: New file. * Makefile (distribute): Add include/caller.h. * elf/Makefile (dl-routines): Add dl-caller. * elf/dl-load.c (_dl_map_object_from_fd): Record l_text_end. * elf/dl-open.c (check_libc_caller): Removed. (dl_open_worker): Use __check_caller instead. * elf/rtld.c (_rtld_global_ro): Initialize _dl_check_caller. (_dl_start_final): Record l_text_end for ld.so map. (dl_main): Record l_text_end for main object and vdso. * include/link.h (struct link_map): Add l_text_end field. * sysdeps/generic/ldsodefs.h (struct rtld_global_ro): Add _dl_check_caller field. Define enum allowmask. Add declaration of _dl_check_caller. * sysdeps/unix/sysv/linux/dl-execstack.c: Also use __check_caller test. --- elf/Makefile | 2 +- elf/dl-caller.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ elf/dl-load.c | 5 +++- elf/dl-open.c | 68 ++------------------------------------------- elf/rtld.c | 12 ++++++++ 5 files changed, 104 insertions(+), 68 deletions(-) create mode 100644 elf/dl-caller.c (limited to 'elf') diff --git a/elf/Makefile b/elf/Makefile index 68474825c7..a366a687de 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -30,7 +30,7 @@ routines = $(dl-routines) dl-open dl-close dl-support dl-iteratephdr \ dl-routines = $(addprefix dl-,load cache lookup object reloc deps \ runtime error init fini debug misc \ version profile conflict tls origin \ - execstack) + execstack caller) all-dl-routines = $(dl-routines) $(sysdep-dl-routines) # But they are absent from the shared libc, because that code is in ld.so. elide-routines.os = $(all-dl-routines) dl-support enbl-secure dl-origin diff --git a/elf/dl-caller.c b/elf/dl-caller.c new file mode 100644 index 0000000000..ddabbd0f2d --- /dev/null +++ b/elf/dl-caller.c @@ -0,0 +1,85 @@ +/* Check whether caller comes from the right place. + Copyright (C) 2004 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include +#include + + +int +attribute_hidden +_dl_check_caller (const void *caller, enum allowmask mask) +{ + static const char expected1[] = LIBC_SO; + static const char expected2[] = LIBDL_SO; +#ifdef LIBPTHREAD_SO + static const char expected3[] = LIBPTHREAD_SO; +#endif + static const char expected4[] = LD_SO; + + for (struct link_map *l = GL(dl_loaded); l != NULL; l = l->l_next) + if (caller >= (const void *) l->l_map_start + && caller < (const void *) l->l_text_end) + { + /* The address falls into this DSO's address range. Check the + name. */ + if ((mask & allow_libc) && strcmp (expected1, l->l_name) == 0) + return 0; + if ((mask & allow_libdl) && strcmp (expected2, l->l_name) == 0) + return 0; +#ifdef LIBPTHREAD_SO + if ((mask & allow_libpthread) && strcmp (expected3, l->l_name) == 0) + return 0; +#endif + if ((mask & allow_ldso) && strcmp (expected4, l->l_name) == 0) + return 0; + + struct libname_list *runp = l->l_libname; + + while (runp != NULL) + { + if ((mask & allow_libc) && strcmp (expected1, runp->name) == 0) + return 0; + if ((mask & allow_libdl) && strcmp (expected2, runp->name) == 0) + return 0; +#ifdef LIBPTHREAD_SO + if ((mask & allow_libpthread) + && strcmp (expected3, runp->name) == 0) + return 0; +#endif + if ((mask & allow_ldso) && strcmp (expected4, runp->name) == 0) + return 0; + + runp = runp->next; + } + + break; + } + + /* Maybe the dynamic linker is not yet on the list. */ + if ((mask & allow_ldso) != 0 + && caller >= (const void *) GL(dl_rtld_map).l_map_start + && caller < (const void *) GL(dl_rtld_map).l_text_end) + return 0; + + /* No valid caller. */ + return 1; +} diff --git a/elf/dl-load.c b/elf/dl-load.c index 1a3c9c5ed9..162fb30af3 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -1110,7 +1110,7 @@ cannot allocate TLS data structures for initial thread"); unallocated. Then jump into the normal segment-mapping loop to handle the portion of the segment past the end of the file mapping. */ - __mprotect ((caddr_t) (l->l_addr + c->mapend), + __mprotect ((caddr_t) l->l_text_end, loadcmds[nloadcmds - 1].allocend - c->mapend, PROT_NONE); @@ -1146,6 +1146,9 @@ cannot allocate TLS data structures for initial thread"); goto map_error; postmap: + if (c->prot & PROT_EXEC) + l->l_text_end = l->l_addr + c->mapend; + if (l->l_phdr == 0 && (ElfW(Off)) c->mapoff <= header->e_phoff && ((size_t) (c->mapend - c->mapstart + c->mapoff) diff --git a/elf/dl-open.c b/elf/dl-open.c index 47db84c867..70f2fb20bc 100644 --- a/elf/dl-open.c +++ b/elf/dl-open.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include @@ -156,68 +156,6 @@ add_to_global (struct link_map *new) } -#ifdef SHARED -static int -internal_function -check_libc_caller (const void *caller) -{ - static const char expected1[] = LIBC_SO; - static const char expected2[] = LIBDL_SO; - - /* If we already know the address ranges, just test. */ - static const void *expected1_from; - static const void *expected1_to; - static const void *expected2_from; - static const void *expected2_to; - - if (expected1_from == NULL) - { - /* The only other DSO which is allowed to call these functions is - libdl. Find the address range containing the caller. */ - struct link_map *l; - - for (l = GL(dl_loaded); l != NULL; l = l->l_next) - if (strcmp (expected1, l->l_name) == 0) - { - is_1: - expected1_from = (const void *) l->l_map_start; - expected1_to = (const void *) l->l_map_end; - } - else if (strcmp (expected2, l->l_name) == 0) - { - is_2: - expected2_from = (const void *) l->l_map_start; - expected2_to = (const void *) l->l_map_end; - } - else - { - struct libname_list *runp = l->l_libname; - - while (runp != NULL) - { - if (strcmp (expected1, runp->name) == 0) - goto is_1; - else if (strcmp (expected2, runp->name) == 0) - goto is_2; - - runp = runp->next; - } - } - - assert (expected1_from != NULL); - } - - /* When there would be more than two expected caller we could use an - array for the values but for now this is cheaper. */ - if ((caller >= expected1_from && caller < expected1_to) - || (caller >= expected2_from && caller < expected2_to)) - return 0; - - return 1; -} -#endif - - static void dl_open_worker (void *a) { @@ -232,11 +170,9 @@ dl_open_worker (void *a) bool any_tls; #endif -#ifdef SHARED /* Check whether _dl_open() has been called from a valid DSO. */ - if (check_libc_caller (args->caller_dl_open) != 0) + if (__check_caller (args->caller_dl_open, allow_libc|allow_libdl) != 0) GLRO(dl_signal_error) (0, "dlopen", NULL, N_("invalid caller")); -#endif /* Maybe we have to expand a DST. */ dst = strchr (file, '$'); diff --git a/elf/rtld.c b/elf/rtld.c index 139358c80d..31521c6484 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -142,6 +142,7 @@ struct rtld_global_ro _rtld_global_ro attribute_relro = ._dl_start_profile = _dl_start_profile, ._dl_mcount = _dl_mcount_internal, ._dl_lookup_symbol_x = _dl_lookup_symbol_x, + ._dl_check_caller = _dl_check_caller }; /* If we would use strong_alias here the compiler would see a non-hidden definition. This would undo the effect of the previous @@ -208,6 +209,7 @@ static ElfW(Addr) _dl_start_final (void *arg, /* These defined magically in the linker script. */ extern char _begin[] attribute_hidden; +extern char _etext[] attribute_hidden; extern char _end[] attribute_hidden; @@ -268,6 +270,7 @@ _dl_start_final (void *arg, struct dl_start_final_info *info) GL(dl_rtld_map).l_opencount = 1; GL(dl_rtld_map).l_map_start = (ElfW(Addr)) _begin; GL(dl_rtld_map).l_map_end = (ElfW(Addr)) _end; + GL(dl_rtld_map).l_text_end = (ElfW(Addr)) _etext; /* Copy the TLS related data if necessary. */ #if USE_TLS && !defined DONT_USE_BOOTSTRAP_MAP # if USE___THREAD @@ -899,6 +902,7 @@ of this helper program; chances are you did not intend to run this program.\n\ } GL(dl_loaded)->l_map_end = 0; + GL(dl_loaded)->l_text_end = 0; /* Perhaps the executable has no PT_LOAD header entries at all. */ GL(dl_loaded)->l_map_start = ~0; /* We opened the file, account for it. */ @@ -969,6 +973,8 @@ of this helper program; chances are you did not intend to run this program.\n\ allocend = GL(dl_loaded)->l_addr + ph->p_vaddr + ph->p_memsz; if (GL(dl_loaded)->l_map_end < allocend) GL(dl_loaded)->l_map_end = allocend; + if ((ph->p_flags & PF_X) && allocend > GL(dl_loaded)->l_text_end) + GL(dl_loaded)->l_text_end = allocend; } break; #ifdef USE_TLS @@ -1012,6 +1018,8 @@ of this helper program; chances are you did not intend to run this program.\n\ #endif if (! GL(dl_loaded)->l_map_end) GL(dl_loaded)->l_map_end = ~0; + if (! GL(dl_loaded)->l_text_end) + GL(dl_loaded)->l_text_end = ~0; if (! GL(dl_rtld_map).l_libname && GL(dl_rtld_map).l_name) { /* We were invoked directly, so the program might not have a @@ -1271,11 +1279,15 @@ ERROR: ld.so: object '%s' from %s cannot be preloaded: ignored.\n", l->l_addr = ph->p_vaddr; else if (ph->p_vaddr + ph->p_memsz >= l->l_map_end) l->l_map_end = ph->p_vaddr + ph->p_memsz; + else if ((ph->p_flags & PF_X) + && ph->p_vaddr + ph->p_memsz >= l->l_text_end) + l->l_text_end = ph->p_vaddr + ph->p_memsz; } } l->l_map_start = (ElfW(Addr)) GLRO(dl_sysinfo_dso); l->l_addr = l->l_map_start - l->l_addr; l->l_map_end += l->l_addr; + l->l_text_end += l->l_addr; l->l_ld = (void *) ((ElfW(Addr)) l->l_ld + l->l_addr); elf_get_dynamic_info (l, dyn_temp); _dl_setup_hash (l); -- cgit v1.2.3