Back to index

glibc  2.9
dl-execstack.c
Go to the documentation of this file.
00001 /* Stack executability handling for GNU dynamic linker.  Linux version.
00002    Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 #include <ldsodefs.h>
00021 #include <sys/mman.h>
00022 #include <errno.h>
00023 #include <libintl.h>
00024 #include <stdbool.h>
00025 #include <stackinfo.h>
00026 #include <caller.h>
00027 #include <sysdep.h>
00028 
00029 #include <kernel-features.h>
00030 
00031 
00032 extern int __stack_prot attribute_relro attribute_hidden;
00033 
00034 
00035 int
00036 internal_function
00037 _dl_make_stack_executable (void **stack_endp)
00038 {
00039   /* This gives us the highest/lowest page that needs to be changed.  */
00040   uintptr_t page = ((uintptr_t) *stack_endp
00041                   & -(intptr_t) GLRO(dl_pagesize));
00042   int result = 0;
00043 
00044   /* Challenge the caller.  */
00045   if (__builtin_expect (__check_caller (RETURN_ADDRESS (0),
00046                                    allow_ldso|allow_libpthread) != 0, 0)
00047       || __builtin_expect (*stack_endp != __libc_stack_end, 0))
00048     return EPERM;
00049 
00050   /* Newer Linux kernels support a flag to make our job easy.  */
00051 #if defined  PROT_GROWSDOWN || defined PROT_GROWSUP
00052 # if __ASSUME_PROT_GROWSUPDOWN == 0
00053   static bool no_growsupdown;
00054   if (! no_growsupdown)
00055 # endif
00056     {
00057       if (__builtin_expect (__mprotect ((void *) page, GLRO(dl_pagesize),
00058                                    __stack_prot) == 0, 1))
00059        goto return_success;
00060 # if __ASSUME_PROT_GROWSUPDOWN == 0
00061       if (errno == EINVAL)
00062        no_growsupdown = true;
00063       else
00064 # endif
00065        {
00066          result = errno;
00067          goto out;
00068        }
00069     }
00070 #endif
00071 
00072   /* There is always a hole in the address space below the bottom of the
00073      stack.  So when we make an mprotect call that starts below the bottom
00074      of the stack, it will include the hole and fail with ENOMEM.
00075 
00076      We start with a random guess at how deep the stack might have gotten
00077      so as to have extended the GROWSDOWN mapping to lower pages.  */
00078 
00079 #if __ASSUME_PROT_GROWSUPDOWN == 0
00080   size_t size = GLRO(dl_pagesize) * 8;
00081 
00082 # if _STACK_GROWS_DOWN
00083   page = page + GLRO(dl_pagesize) - size;
00084   while (1)
00085     {
00086       if (__mprotect ((void *) page, size,
00087                     __stack_prot & ~PROT_GROWSDOWN) == 0)
00088        /* We got this chunk changed; loop to do another chunk below.  */
00089        page -= size;
00090       else
00091        {
00092          if (errno != ENOMEM)      /* Unexpected failure mode.  */
00093            {
00094              result = errno;
00095              goto out;
00096            }
00097 
00098          if (size == GLRO(dl_pagesize))
00099            /* We just tried to mprotect the top hole page and failed.
00100               We are done.  */
00101            break;
00102 
00103          /* Our mprotect call failed because it started below the lowest
00104             stack page.  Try again on just the top half of that region.  */
00105          size /= 2;
00106          page += size;
00107        }
00108     }
00109 
00110 # elif _STACK_GROWS_UP
00111   while (1)
00112     {
00113       if (__mprotect ((void *) page, size, __stack_prot & ~PROT_GROWSUP) == 0)
00114        /* We got this chunk changed; loop to do another chunk below.  */
00115        page += size;
00116       else
00117        {
00118          if (errno != ENOMEM)      /* Unexpected failure mode.  */
00119            {
00120              result = errno;
00121              goto out;
00122            }
00123 
00124          if (size == GLRO(dl_pagesize))
00125            /* We just tried to mprotect the lowest hole page and failed.
00126               We are done.  */
00127            break;
00128 
00129          /* Our mprotect call failed because it extended past the highest
00130             stack page.  Try again on just the bottom half of that region.  */
00131          size /= 2;
00132        }
00133     }
00134 
00135 # else
00136 #  error "Define either _STACK_GROWS_DOWN or _STACK_GROWS_UP"
00137 # endif
00138 #endif
00139 
00140  return_success:
00141   /* Clear the address.  */
00142   *stack_endp = NULL;
00143 
00144   /* Remember that we changed the permission.  */
00145   GL(dl_stack_flags) |= PF_X;
00146 
00147  out:
00148 #ifdef check_consistency
00149   check_consistency ();
00150 #endif
00151 
00152   return result;
00153 }
00154 rtld_hidden_def (_dl_make_stack_executable)