Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
2003-09-23 Roland McGrath <roland@redhat.com>
	* sysdeps/generic/ldsodefs.h (struct rtld_global): Add _dl_stack_flags
	and _dl_make_stack_executable_hook.  Declare _dl_make_stack_executable.
	* elf/rtld.c (_rtld_global): Add initializer for _dl_stack_flags.
	(dl_main): Reset _dl_stack_flags according to PT_GNU_STACK phdr.
	Initialize _dl_make_stack_executable_hook.
	* elf/dl-support.c: Define those new variables.
	(_dl_non_dynamic_init): Scan phdrs for PT_GNU_STACK.
	(_dl_phdr): Fix type.
	* elf/dl-load.c (_dl_map_object_from_fd): Grok PT_GNU_STACK phdr and
	enable execute permission for the stack if necessary.
	* sysdeps/generic/dl-execstack.c: New file.
	* elf/Makefile (dl-routines): Add it.
	* elf/Versions (ld: GLIBC_PRIVATE): Add _dl_make_stack_executable.
	* sysdeps/unix/sysv/linux/dl-execstack.c: New file.
  • Loading branch information
Roland McGrath committed Sep 23, 2003
1 parent 1deb577 commit ecdeaac
Show file tree
Hide file tree
Showing 7 changed files with 210 additions and 1 deletion.
1 change: 1 addition & 0 deletions elf/Versions
Expand Up @@ -54,5 +54,6 @@ ld {
_rtld_global; _dl_tls_symaddr; _dl_allocate_tls; _dl_deallocate_tls;
_dl_get_tls_static_info; _dl_allocate_tls_init;
_dl_get_origin; _dl_tls_setup; _dl_rtld_di_serinfo;
_dl_make_stack_executable;
}
}
20 changes: 20 additions & 0 deletions elf/dl-load.c
Expand Up @@ -904,6 +904,9 @@ _dl_map_object_from_fd (const char *name, int fd, struct filebuf *fbp,
}
}

/* Presumed absent PT_GNU_STACK. */
uint_fast16_t stack_flags = PF_R|PF_W|PF_X;

{
/* Scan the program header table, collecting its load commands. */
struct loadcmd
Expand Down Expand Up @@ -1058,6 +1061,10 @@ cannot allocate TLS data structures for initial thread");
errstring = N_("cannot handle TLS data");
goto call_lose;
break;

case PT_GNU_STACK:
stack_flags = ph->p_flags;
break;
}

if (__builtin_expect (nloadcmds == 0, 0))
Expand Down Expand Up @@ -1334,6 +1341,19 @@ cannot allocate TLS data structures for initial thread");
l->l_dev = st.st_dev;
l->l_ino = st.st_ino;

if (__builtin_expect ((stack_flags &~ GL(dl_stack_flags)) & PF_X, 0))
{
/* The stack is presently not executable, but this module
requires that it be executable. */
errval = (*GL(dl_make_stack_executable_hook)) ();
if (errval)
{
errstring = N_("\
cannot enable executable stack as shared object requires");
goto call_lose;
}
}

/* When we profile the SONAME might be needed for something else but
loading. Add it right away. */
if (__builtin_expect (GL(dl_profile) != NULL, 0)
Expand Down
19 changes: 19 additions & 0 deletions elf/dl-support.c
Expand Up @@ -129,6 +129,16 @@ ElfW(Phdr) *_dl_phdr;
size_t _dl_phnum;
unsigned long int _dl_hwcap __attribute__ ((nocommon));

/* Prevailing state of the stack, PF_X indicating it's executable. */
ElfW(Word) _dl_stack_flags = PF_R|PF_W|PF_X;

/* If loading a shared object requires that we make the stack executable
when it was not, we do it by calling this function.
It returns an errno code or zero on success. */
int (*_dl_make_stack_executable_hook) (void) internal_function
= _dl_make_stack_executable;


#ifdef NEED_DL_SYSINFO
/* Needed for improved syscall handling on at least x86/Linux. */
uintptr_t _dl_sysinfo = DL_SYSINFO_DEFAULT;
Expand Down Expand Up @@ -264,6 +274,15 @@ _dl_non_dynamic_init (void)
/* Now determine the length of the platform string. */
if (_dl_platform != NULL)
_dl_platformlen = strlen (_dl_platform);

/* Scan for a program header telling us the stack is nonexecutable. */
if (_dl_phdr != NULL)
for (uint_fast16_t i = 0; i < _dl_phnum; ++i)
if (_dl_phdr[i].p_type == PT_GNU_STACK)
{
_dl_stack_flags = _dl_phdr[i].p_flags;
break;
}
}


Expand Down
11 changes: 10 additions & 1 deletion elf/rtld.c
Expand Up @@ -98,6 +98,8 @@ struct rtld_global _rtld_global =
._dl_fpu_control = _FPU_DEFAULT,
._dl_correct_cache_id = _DL_CACHE_DEFAULT_ID,
._dl_hwcap_mask = HWCAP_IMPORTANT,
/* Default presumption without further information is executable stack. */
._dl_stack_flags = PF_R|PF_W|PF_X,
#ifdef _LIBC_REENTRANT
._dl_load_lock = _RTLD_LOCK_RECURSIVE_INITIALIZER
#endif
Expand Down Expand Up @@ -249,7 +251,7 @@ _dl_start_final (void *arg, struct dl_start_final_info *info)
file access. It will call `dl_main' (below) to do all the real work
of the dynamic linker, and then unwind our frame and run the user
entry point on the same stack we entered on. */
start_addr = _dl_sysdep_start (arg, &dl_main);
start_addr = _dl_sysdep_start (arg, &dl_main);

#ifndef HP_TIMING_NONAVAIL
if (HP_TIMING_AVAIL)
Expand Down Expand Up @@ -903,6 +905,9 @@ of this helper program; chances are you did not intend to run this program.\n\
}
break;
#endif
case PT_GNU_STACK:
GL(dl_stack_flags) = ph->p_flags;
break;
}
#ifdef USE_TLS
/* Adjust the address of the TLS initialization image in case
Expand Down Expand Up @@ -949,6 +954,10 @@ of this helper program; chances are you did not intend to run this program.\n\
_exit (has_interp ? 0 : 2);
}

/* The explicit initialization here is cheaper than processing the reloc
in the _rtld_local definition's initializer. */
GL(dl_make_stack_executable_hook) = &_dl_make_stack_executable;

if (! rtld_is_main)
/* Initialize the data structures for the search paths for shared
objects. */
Expand Down
32 changes: 32 additions & 0 deletions sysdeps/generic/dl-execstack.c
@@ -0,0 +1,32 @@
/* Stack executability handling for GNU dynamic linker. Stub version.
Copyright (C) 2003 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 <ldsodefs.h>
#include <errno.h>

/* There is no portable way to know the bounds of the initial thread's stack
so as to mprotect it. */

int
internal_function
_dl_make_stack_executable (void)
{
return ENOSYS;
}
rtld_hidden_def (_dl_make_stack_executable)
13 changes: 13 additions & 0 deletions sysdeps/generic/ldsodefs.h
Expand Up @@ -355,6 +355,14 @@ struct rtld_global
EXTERN void (*_dl_rtld_unlock_recursive) (void *);
#endif

/* Prevailing state of the stack, PF_X indicating it's executable. */
EXTERN ElfW(Word) _dl_stack_flags;

/* If loading a shared object requires that we make the stack executable
when it was not, we do it by calling this function.
It returns an errno code or zero on success. */
EXTERN int (*_dl_make_stack_executable_hook) (void) internal_function;

/* Keep the conditional TLS members at the end so the layout of the
structure used by !USE_TLS code matches the prefix of the layout in
the USE_TLS rtld. Note that `struct link_map' is conditionally
Expand Down Expand Up @@ -438,6 +446,11 @@ extern void **_dl_initial_error_catch_tsd (void) __attribute__ ((const))
attribute_hidden;
#endif

/* This is the initial value of GL(dl_make_stack_executable_hook).
A threads library can change it. */
extern int _dl_make_stack_executable (void) internal_function;
rtld_hidden_proto (_dl_make_stack_executable)

/* Parameters passed to the dynamic linker. */
extern int _dl_argc attribute_hidden;
extern char **_dl_argv;
Expand Down
115 changes: 115 additions & 0 deletions sysdeps/unix/sysv/linux/dl-execstack.c
@@ -0,0 +1,115 @@
/* Stack executability handling for GNU dynamic linker. Linux version.
Copyright (C) 2003 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 <ldsodefs.h>
#include <sys/mman.h>
#include <errno.h>
#include <stdbool.h>
#include <stackinfo.h>

extern void *__libc_stack_end;

int
internal_function
_dl_make_stack_executable (void)
{
if (__libc_stack_end == 0)
/* XXX for a DT_NEEDED library that requires the change,
this is not initialized yet!
*/
return ENOSYS;

#if _STACK_GROWS_DOWN
/* This gives us the highest page that needs to be changed. */
uintptr_t page = (uintptr_t) __libc_stack_end & -(intptr_t) GL(dl_pagesize);

/* There is always a hole in the address space below the bottom of the
stack. So when we make an mprotect call that starts below the bottom
of the stack, it will include the hole and fail with ENOMEM.
We start with a random guess at how deep the stack might have gotten
so as to have extended the GROWSDOWN mapping to lower pages. */

size_t size = GL(dl_pagesize) * 8;
page = page + GL(dl_pagesize) - size;
while (1)
{
if (__mprotect ((void *) page, size,
PROT_READ|PROT_WRITE|PROT_EXEC) == 0)
/* We got this chunk changed; loop to do another chunk below. */
page -= size;
else
{
if (errno != ENOMEM) /* Unexpected failure mode. */
return errno;

if (size == GL(dl_pagesize))
/* We just tried to mprotect the top hole page and failed.
We are done. */
break;

/* Our mprotect call failed because it started below the lowest
stack page. Try again on just the top half of that region. */
size /= 2;
page += size;
}
}

#elif _STACK_GROWS_UP

/* This gives us the lowest page that needs to be changed. */
uintptr_t page = (uintptr_t) __libc_stack_end & -(intptr_t) GL(dl_pagesize);

/* There is always a hole in the address space above the top of the
stack. So when we make an mprotect call that spans past the top
of the stack, it will include the hole and fail with ENOMEM.
We start with a random guess at how deep the stack might have gotten
so as to have extended the GROWSUP mapping to higher pages. */

size_t size = GL(dl_pagesize) * 8;
while (1)
{
if (__mprotect ((void *) page, size,
PROT_READ|PROT_WRITE|PROT_EXEC) == 0)
/* We got this chunk changed; loop to do another chunk below. */
page += size;
else
{
if (errno != ENOMEM) /* Unexpected failure mode. */
return errno;

if (size == GL(dl_pagesize))
/* We just tried to mprotect the lowest hole page and failed.
We are done. */
break;

/* Our mprotect call failed because it extended past the highest
stack page. Try again on just the bottom half of that region. */
size /= 2;
}
}

#else
# error "Define either _STACK_GROWS_DOWN or _STACK_GROWS_UP"
#endif

return 0;
}
rtld_hidden_def (_dl_make_stack_executable)

0 comments on commit ecdeaac

Please sign in to comment.