]>
Commit | Line | Data |
---|---|---|
2abf9ff1 RM |
1 | /* Test program for making nonexecutable stacks executable |
2 | on load of a DSO that requires executable stacks. */ | |
3 | ||
4 | #include <dlfcn.h> | |
d9961b6a | 5 | #include <stdbool.h> |
2abf9ff1 RM |
6 | #include <stdio.h> |
7 | #include <string.h> | |
8 | #include <unistd.h> | |
9 | #include <error.h> | |
9c6ea9fa | 10 | #include <stackinfo.h> |
2abf9ff1 RM |
11 | |
12 | static void | |
13 | print_maps (void) | |
14 | { | |
15 | #if 0 | |
16 | char *cmd = NULL; | |
17 | asprintf (&cmd, "cat /proc/%d/maps", getpid ()); | |
18 | system (cmd); | |
19 | free (cmd); | |
20 | #endif | |
21 | } | |
22 | ||
23 | static void deeper (void (*f) (void)); | |
24 | ||
25 | #if USE_PTHREADS | |
26 | # include <pthread.h> | |
27 | ||
28 | static void * | |
29 | tryme_thread (void *f) | |
30 | { | |
31 | (*((void (*) (void)) f)) (); | |
32 | ||
33 | return 0; | |
34 | } | |
35 | ||
36 | static pthread_barrier_t startup_barrier, go_barrier; | |
37 | static void * | |
38 | waiter_thread (void *arg) | |
39 | { | |
40 | void **f = arg; | |
41 | pthread_barrier_wait (&startup_barrier); | |
42 | pthread_barrier_wait (&go_barrier); | |
43 | ||
44 | (*((void (*) (void)) *f)) (); | |
45 | ||
46 | return 0; | |
47 | } | |
48 | #endif | |
49 | ||
d9961b6a UD |
50 | static bool allow_execstack = true; |
51 | ||
52 | ||
2abf9ff1 RM |
53 | static int |
54 | do_test (void) | |
55 | { | |
d9961b6a UD |
56 | /* Check whether SELinux is enabled and disallows executable stacks. */ |
57 | FILE *fp = fopen ("/selinux/enforce", "r"); | |
58 | if (fp != NULL) | |
59 | { | |
60 | char *line = NULL; | |
61 | size_t linelen = 0; | |
62 | ||
63 | bool enabled = false; | |
64 | ssize_t n = getline (&line, &linelen, fp); | |
65 | if (n > 0 && line[0] != '0') | |
66 | enabled = true; | |
67 | ||
68 | fclose (fp); | |
69 | ||
70 | if (enabled) | |
71 | { | |
72 | fp = fopen ("/selinux/booleans/allow_execstack", "r"); | |
73 | if (fp != NULL) | |
74 | { | |
75 | n = getline (&line, &linelen, fp); | |
76 | if (n > 0 && line[0] == '0') | |
77 | allow_execstack = false; | |
78 | } | |
79 | ||
80 | fclose (fp); | |
81 | } | |
82 | } | |
83 | ||
84 | printf ("executable stacks %sallowed\n", allow_execstack ? "" : "not "); | |
85 | ||
26e059c1 | 86 | static void *f; /* Address of this is used in other threads. */ |
2abf9ff1 RM |
87 | |
88 | #if USE_PTHREADS | |
89 | /* Create some threads while stacks are nonexecutable. */ | |
90 | #define N 5 | |
91 | pthread_t thr[N]; | |
92 | ||
93 | pthread_barrier_init (&startup_barrier, NULL, N + 1); | |
94 | pthread_barrier_init (&go_barrier, NULL, N + 1); | |
95 | ||
96 | for (int i = 0; i < N; ++i) | |
97 | { | |
98 | int rc = pthread_create (&thr[i], NULL, &waiter_thread, &f); | |
99 | if (rc) | |
100 | error (1, rc, "pthread_create"); | |
101 | } | |
102 | ||
103 | /* Make sure they are all there using their stacks. */ | |
104 | pthread_barrier_wait (&startup_barrier); | |
105 | puts ("threads waiting"); | |
106 | #endif | |
107 | ||
108 | print_maps (); | |
109 | ||
9c6ea9fa SP |
110 | #if USE_PTHREADS |
111 | void *old_stack_addr, *new_stack_addr; | |
112 | size_t stack_size; | |
113 | pthread_t me = pthread_self (); | |
114 | pthread_attr_t attr; | |
115 | int ret = 0; | |
116 | ||
117 | ret = pthread_getattr_np (me, &attr); | |
118 | if (ret) | |
119 | { | |
120 | printf ("before execstack: pthread_getattr_np returned error: %s\n", | |
121 | strerror (ret)); | |
122 | return 1; | |
123 | } | |
124 | ||
125 | ret = pthread_attr_getstack (&attr, &old_stack_addr, &stack_size); | |
126 | if (ret) | |
127 | { | |
128 | printf ("before execstack: pthread_attr_getstack returned error: %s\n", | |
129 | strerror (ret)); | |
130 | return 1; | |
131 | } | |
132 | # if _STACK_GROWS_DOWN | |
133 | old_stack_addr += stack_size; | |
134 | # else | |
135 | old_stack_addr -= stack_size; | |
136 | # endif | |
137 | #endif | |
138 | ||
2abf9ff1 RM |
139 | /* Loading this module should force stacks to become executable. */ |
140 | void *h = dlopen ("tst-execstack-mod.so", RTLD_LAZY); | |
141 | if (h == NULL) | |
142 | { | |
143 | printf ("cannot load: %s\n", dlerror ()); | |
d9961b6a | 144 | return allow_execstack; |
2abf9ff1 RM |
145 | } |
146 | ||
147 | f = dlsym (h, "tryme"); | |
148 | if (f == NULL) | |
149 | { | |
150 | printf ("symbol not found: %s\n", dlerror ()); | |
151 | return 1; | |
152 | } | |
153 | ||
154 | /* Test if that really made our stack executable. | |
155 | The `tryme' function should crash if not. */ | |
156 | ||
157 | (*((void (*) (void)) f)) (); | |
158 | ||
159 | print_maps (); | |
160 | ||
9c6ea9fa SP |
161 | #if USE_PTHREADS |
162 | ret = pthread_getattr_np (me, &attr); | |
163 | if (ret) | |
164 | { | |
165 | printf ("after execstack: pthread_getattr_np returned error: %s\n", | |
166 | strerror (ret)); | |
167 | return 1; | |
168 | } | |
169 | ||
170 | ret = pthread_attr_getstack (&attr, &new_stack_addr, &stack_size); | |
171 | if (ret) | |
172 | { | |
173 | printf ("after execstack: pthread_attr_getstack returned error: %s\n", | |
174 | strerror (ret)); | |
175 | return 1; | |
176 | } | |
177 | ||
178 | # if _STACK_GROWS_DOWN | |
179 | new_stack_addr += stack_size; | |
180 | # else | |
181 | new_stack_addr -= stack_size; | |
182 | # endif | |
183 | ||
184 | /* It is possible that the dlopen'd module may have been mmapped just below | |
185 | the stack. The stack size is taken as MIN(stack rlimit size, end of last | |
186 | vma) in pthread_getattr_np. If rlimit is set high enough, it is possible | |
187 | that the size may have changed. A subsequent call to | |
188 | pthread_attr_getstack returns the size and (bottom - size) as the | |
189 | stacksize and stackaddr respectively. If the size changes due to the | |
190 | above, then both stacksize and stackaddr can change, but the stack bottom | |
191 | should remain the same, which is computed as stackaddr + stacksize. */ | |
192 | if (old_stack_addr != new_stack_addr) | |
193 | { | |
194 | printf ("Stack end changed, old: %p, new: %p\n", | |
195 | old_stack_addr, new_stack_addr); | |
196 | return 1; | |
197 | } | |
198 | printf ("Stack address remains the same: %p\n", old_stack_addr); | |
199 | #endif | |
200 | ||
2abf9ff1 RM |
201 | /* Test that growing the stack region gets new executable pages too. */ |
202 | deeper ((void (*) (void)) f); | |
203 | ||
204 | print_maps (); | |
205 | ||
206 | #if USE_PTHREADS | |
207 | /* Test that a fresh thread now gets an executable stack. */ | |
208 | { | |
209 | pthread_t th; | |
210 | int rc = pthread_create (&th, NULL, &tryme_thread, f); | |
211 | if (rc) | |
212 | error (1, rc, "pthread_create"); | |
213 | } | |
214 | ||
215 | puts ("threads go"); | |
216 | /* The existing threads' stacks should have been changed. | |
217 | Let them run to test it. */ | |
218 | pthread_barrier_wait (&go_barrier); | |
219 | ||
3005703b | 220 | pthread_exit ((void *) (long int) (! allow_execstack)); |
2abf9ff1 RM |
221 | #endif |
222 | ||
d9961b6a | 223 | return ! allow_execstack; |
2abf9ff1 RM |
224 | } |
225 | ||
226 | static void | |
227 | deeper (void (*f) (void)) | |
228 | { | |
229 | char stack[1100 * 1024]; | |
230 | memfrob (stack, sizeof stack); | |
231 | (*f) (); | |
232 | memfrob (stack, sizeof stack); | |
233 | } | |
234 | ||
235 | ||
36fe25fd | 236 | #include <support/test-driver.c> |