]>
Commit | Line | Data |
---|---|---|
ecdeaac0 RM |
1 | /* Stack executability handling for GNU dynamic linker. Linux version. |
2 | Copyright (C) 2003 Free Software Foundation, Inc. | |
3 | This file is part of the GNU C Library. | |
4 | ||
5 | The GNU C Library is free software; you can redistribute it and/or | |
6 | modify it under the terms of the GNU Lesser General Public | |
7 | License as published by the Free Software Foundation; either | |
8 | version 2.1 of the License, or (at your option) any later version. | |
9 | ||
10 | The GNU C Library is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | Lesser General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU Lesser General Public | |
16 | License along with the GNU C Library; if not, write to the Free | |
17 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | |
18 | 02111-1307 USA. */ | |
19 | ||
20 | #include <ldsodefs.h> | |
21 | #include <sys/mman.h> | |
22 | #include <errno.h> | |
23 | #include <stdbool.h> | |
24 | #include <stackinfo.h> | |
25 | ||
26 | extern void *__libc_stack_end; | |
27 | ||
28 | int | |
29 | internal_function | |
30 | _dl_make_stack_executable (void) | |
31 | { | |
ecdeaac0 RM |
32 | #if _STACK_GROWS_DOWN |
33 | /* This gives us the highest page that needs to be changed. */ | |
34 | uintptr_t page = (uintptr_t) __libc_stack_end & -(intptr_t) GL(dl_pagesize); | |
35 | ||
e95dc10c RM |
36 | /* Newer Linux kernels support a flag to make our job easy. */ |
37 | # ifdef PROT_GROWSDOWN | |
38 | static bool no_growsdown; | |
39 | if (! no_growsdown) | |
40 | { | |
41 | if (__mprotect ((void *) page, GL(dl_pagesize), | |
42 | PROT_READ|PROT_WRITE|PROT_EXEC|PROT_GROWSDOWN) == 0) | |
43 | return 0; | |
44 | if (errno != EINVAL) | |
45 | return errno; | |
46 | no_growsdown = true; | |
47 | } | |
48 | # endif | |
49 | ||
ecdeaac0 RM |
50 | /* There is always a hole in the address space below the bottom of the |
51 | stack. So when we make an mprotect call that starts below the bottom | |
52 | of the stack, it will include the hole and fail with ENOMEM. | |
53 | ||
54 | We start with a random guess at how deep the stack might have gotten | |
55 | so as to have extended the GROWSDOWN mapping to lower pages. */ | |
56 | ||
57 | size_t size = GL(dl_pagesize) * 8; | |
58 | page = page + GL(dl_pagesize) - size; | |
59 | while (1) | |
60 | { | |
61 | if (__mprotect ((void *) page, size, | |
62 | PROT_READ|PROT_WRITE|PROT_EXEC) == 0) | |
63 | /* We got this chunk changed; loop to do another chunk below. */ | |
64 | page -= size; | |
65 | else | |
66 | { | |
67 | if (errno != ENOMEM) /* Unexpected failure mode. */ | |
68 | return errno; | |
69 | ||
70 | if (size == GL(dl_pagesize)) | |
71 | /* We just tried to mprotect the top hole page and failed. | |
72 | We are done. */ | |
73 | break; | |
74 | ||
75 | /* Our mprotect call failed because it started below the lowest | |
76 | stack page. Try again on just the top half of that region. */ | |
77 | size /= 2; | |
78 | page += size; | |
79 | } | |
80 | } | |
81 | ||
82 | #elif _STACK_GROWS_UP | |
83 | ||
84 | /* This gives us the lowest page that needs to be changed. */ | |
85 | uintptr_t page = (uintptr_t) __libc_stack_end & -(intptr_t) GL(dl_pagesize); | |
86 | ||
e95dc10c RM |
87 | /* Newer Linux kernels support a flag to make our job easy. */ |
88 | # ifdef PROT_GROWSUP | |
89 | static bool no_growsup; | |
90 | if (! no_growsup) | |
91 | { | |
92 | if (__mprotect ((void *) page, GL(dl_pagesize), | |
93 | PROT_READ|PROT_WRITE|PROT_EXEC|PROT_GROWSUP) == 0) | |
94 | return 0; | |
95 | if (errno != EINVAL) | |
96 | return errno; | |
97 | no_growsup = true; | |
98 | } | |
99 | # endif | |
100 | ||
ecdeaac0 RM |
101 | /* There is always a hole in the address space above the top of the |
102 | stack. So when we make an mprotect call that spans past the top | |
103 | of the stack, it will include the hole and fail with ENOMEM. | |
104 | ||
105 | We start with a random guess at how deep the stack might have gotten | |
106 | so as to have extended the GROWSUP mapping to higher pages. */ | |
107 | ||
108 | size_t size = GL(dl_pagesize) * 8; | |
109 | while (1) | |
110 | { | |
111 | if (__mprotect ((void *) page, size, | |
112 | PROT_READ|PROT_WRITE|PROT_EXEC) == 0) | |
113 | /* We got this chunk changed; loop to do another chunk below. */ | |
114 | page += size; | |
115 | else | |
116 | { | |
117 | if (errno != ENOMEM) /* Unexpected failure mode. */ | |
118 | return errno; | |
119 | ||
120 | if (size == GL(dl_pagesize)) | |
121 | /* We just tried to mprotect the lowest hole page and failed. | |
122 | We are done. */ | |
123 | break; | |
124 | ||
125 | /* Our mprotect call failed because it extended past the highest | |
126 | stack page. Try again on just the bottom half of that region. */ | |
127 | size /= 2; | |
128 | } | |
129 | } | |
130 | ||
131 | #else | |
132 | # error "Define either _STACK_GROWS_DOWN or _STACK_GROWS_UP" | |
133 | #endif | |
134 | ||
69c9fa04 UD |
135 | /* Remember that we changed the permission. */ |
136 | GL(dl_stack_flags) |= PF_X; | |
137 | ||
ecdeaac0 RM |
138 | return 0; |
139 | } | |
140 | rtld_hidden_def (_dl_make_stack_executable) |