diff --git a/ChangeLog b/ChangeLog index 7cf2f43d1c..c5a8c57507 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +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. + 2004-03-26 Richard Henderson * sysdeps/alpha/Makefile (sysdep_routines): Merge divrem diff --git a/Makefile b/Makefile index 5089063a55..a422ffd22b 100644 --- a/Makefile +++ b/Makefile @@ -282,7 +282,7 @@ distribute := README README.libm INSTALL FAQ FAQ.in NOTES NEWS BUGS \ rpm/template rpm/rpmrc abi-tags stub-tag.h \ test-skeleton.c include/des.h include/libc-internal.h \ include/shlib-compat.h include/pthread.h Versions.def \ - cppflags-iterator.mk tls.make.c \ + cppflags-iterator.mk tls.make.c include/caller.h \ include/stubs-prologue.h include/gnu/stubs.h \ include/atomic.h bits/atomic.h symbol-hacks.h \ INTERFACE CONFORMANCE NAMESPACE LICENSES \ 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); diff --git a/include/link.h b/include/link.h index f410f10daf..5829f3c98f 100644 --- a/include/link.h +++ b/include/link.h @@ -208,6 +208,8 @@ struct link_map /* Start and finish of memory map for this object. l_map_start need not be the same as l_addr. */ ElfW(Addr) l_map_start, l_map_end; + /* End of the executable part of the mapping. */ + ElfW(Addr) l_text_end; /* Default array for 'l_scope'. */ struct r_scope_elem *l_scope_mem[4]; diff --git a/nptl/ChangeLog b/nptl/ChangeLog index b29f3b30f5..43dfc3b63c 100644 --- a/nptl/ChangeLog +++ b/nptl/ChangeLog @@ -1,3 +1,8 @@ +2004-03-26 Ulrich Drepper + + * allocatestack.c (_make_stacks_executable): Call + _dl_make_stack_executable first. + 2004-03-24 Roland McGrath * sysdeps/i386/pthread_spin_lock.c (pthread_spin_lock): Use "m" diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c index 7983bed273..4b5af82f06 100644 --- a/nptl/allocatestack.c +++ b/nptl/allocatestack.c @@ -676,9 +676,10 @@ int internal_function __make_stacks_executable (void **stack_endp) { - /* Challenge the caller. */ - if (*stack_endp != __libc_stack_end) - return EPERM; + /* First the main thread's stack. */ + int err = _dl_make_stack_executable (stack_endp); + if (err != 0) + return err; #ifdef NEED_SEPARATE_REGISTER_STACK const size_t pagemask = ~(__getpagesize () - 1); @@ -686,7 +687,6 @@ __make_stacks_executable (void **stack_endp) lll_lock (stack_cache_lock); - int err = 0; list_t *runp; list_for_each (runp, &stack_used) { @@ -716,9 +716,6 @@ __make_stacks_executable (void **stack_endp) lll_unlock (stack_cache_lock); - if (err == 0) - err = _dl_make_stack_executable (stack_endp); - return err; } diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h index 4ff8bbf217..53c3290e01 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -171,6 +171,17 @@ struct libname_list }; +/* Bit masks for the objects which valid callers can come from to + functions with restricted interface. */ +enum allowmask + { + allow_libc = 1, + allow_libdl = 2, + allow_libpthread = 4, + allow_ldso = 8 + }; + + /* Test whether given NAME matches any of the names of the given object. */ extern int _dl_name_match_p (const char *__name, struct link_map *__map) internal_function; @@ -492,6 +503,7 @@ struct rtld_global_ro const struct r_found_version *, int, int, struct link_map *); + int (*_dl_check_caller) (const void *, enum allowmask); }; # define __rtld_global_attribute__ @@ -878,6 +890,10 @@ extern size_t _dl_dst_count (const char *name, int is_path) attribute_hidden; extern char *_dl_dst_substitute (struct link_map *l, const char *name, char *result, int is_path) attribute_hidden; +/* Check validity of the caller. */ +extern int _dl_check_caller (const void *caller, enum allowmask mask) + attribute_hidden; + __END_DECLS #endif /* ldsodefs.h */ diff --git a/sysdeps/unix/sysv/linux/dl-execstack.c b/sysdeps/unix/sysv/linux/dl-execstack.c index d3b048192c..248196040a 100644 --- a/sysdeps/unix/sysv/linux/dl-execstack.c +++ b/sysdeps/unix/sysv/linux/dl-execstack.c @@ -20,8 +20,10 @@ #include #include #include +#include #include #include +#include #include "kernel-features.h" @@ -31,13 +33,14 @@ internal_function _dl_make_stack_executable (void **stack_endp) { /* This gives us the highest/lowest page that needs to be changed. */ - uintptr_t page = ((uintptr_t) __libc_stack_end + uintptr_t page = ((uintptr_t) *stack_endp & -(intptr_t) GLRO(dl_pagesize)); /* Challenge the caller. */ - if (__builtin_expect (*stack_endp != __libc_stack_end, 0)) + if (__builtin_expect (__check_caller (__builtin_return_address (0), + allow_ldso|allow_libpthread) != 0, 0) + || __builtin_expect (*stack_endp != __libc_stack_end, 0)) return EPERM; - *stack_endp = NULL; #if _STACK_GROWS_DOWN /* Newer Linux kernels support a flag to make our job easy. */ @@ -151,6 +154,9 @@ _dl_make_stack_executable (void **stack_endp) #endif return_success: + /* Clear the address. */ + *stack_endp = NULL; + /* Remember that we changed the permission. */ GL(dl_stack_flags) |= PF_X;