1 | /* Test program for making nonexecutable stacks executable |
2 | on load of a DSO that requires executable stacks. */ |
3 | |
4 | #include <dlfcn.h> |
5 | #include <stdbool.h> |
6 | #include <stdio.h> |
7 | #include <string.h> |
8 | #include <unistd.h> |
9 | #include <error.h> |
10 | #include <stackinfo.h> |
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 | |
50 | static bool allow_execstack = true; |
51 | |
52 | |
53 | static int |
54 | do_test (void) |
55 | { |
56 | /* Check whether SELinux is enabled and disallows executable stacks. */ |
57 | FILE *fp = fopen (filename: "/selinux/enforce" , modes: "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 (lineptr: &line, n: &linelen, stream: fp); |
65 | if (n > 0 && line[0] != '0') |
66 | enabled = true; |
67 | |
68 | fclose (stream: fp); |
69 | |
70 | if (enabled) |
71 | { |
72 | fp = fopen (filename: "/selinux/booleans/allow_execstack" , modes: "r" ); |
73 | if (fp != NULL) |
74 | { |
75 | n = getline (lineptr: &line, n: &linelen, stream: fp); |
76 | if (n > 0 && line[0] == '0') |
77 | allow_execstack = false; |
78 | } |
79 | |
80 | fclose (stream: fp); |
81 | } |
82 | } |
83 | |
84 | printf (format: "executable stacks %sallowed\n" , allow_execstack ? "" : "not " ); |
85 | |
86 | static void *f; /* Address of this is used in other threads. */ |
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 | |
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 | |
139 | /* Loading this module should force stacks to become executable. */ |
140 | #if USE_PTHREADS |
141 | const char *soname = "tst-execstack-threads-mod.so" ; |
142 | #else |
143 | const char *soname = "tst-execstack-mod.so" ; |
144 | #endif |
145 | void *h = dlopen (file: soname, RTLD_LAZY); |
146 | if (h == NULL) |
147 | { |
148 | printf (format: "cannot load: %s\n" , dlerror ()); |
149 | return allow_execstack; |
150 | } |
151 | |
152 | f = dlsym (handle: h, name: "tryme" ); |
153 | if (f == NULL) |
154 | { |
155 | printf (format: "symbol not found: %s\n" , dlerror ()); |
156 | return 1; |
157 | } |
158 | |
159 | /* Test if that really made our stack executable. |
160 | The `tryme' function should crash if not. */ |
161 | |
162 | (*((void (*) (void)) f)) (); |
163 | |
164 | print_maps (); |
165 | |
166 | #if USE_PTHREADS |
167 | ret = pthread_getattr_np (me, &attr); |
168 | if (ret) |
169 | { |
170 | printf ("after execstack: pthread_getattr_np returned error: %s\n" , |
171 | strerror (ret)); |
172 | return 1; |
173 | } |
174 | |
175 | ret = pthread_attr_getstack (&attr, &new_stack_addr, &stack_size); |
176 | if (ret) |
177 | { |
178 | printf ("after execstack: pthread_attr_getstack returned error: %s\n" , |
179 | strerror (ret)); |
180 | return 1; |
181 | } |
182 | |
183 | # if _STACK_GROWS_DOWN |
184 | new_stack_addr += stack_size; |
185 | # else |
186 | new_stack_addr -= stack_size; |
187 | # endif |
188 | |
189 | /* It is possible that the dlopen'd module may have been mmapped just below |
190 | the stack. The stack size is taken as MIN(stack rlimit size, end of last |
191 | vma) in pthread_getattr_np. If rlimit is set high enough, it is possible |
192 | that the size may have changed. A subsequent call to |
193 | pthread_attr_getstack returns the size and (bottom - size) as the |
194 | stacksize and stackaddr respectively. If the size changes due to the |
195 | above, then both stacksize and stackaddr can change, but the stack bottom |
196 | should remain the same, which is computed as stackaddr + stacksize. */ |
197 | if (old_stack_addr != new_stack_addr) |
198 | { |
199 | printf ("Stack end changed, old: %p, new: %p\n" , |
200 | old_stack_addr, new_stack_addr); |
201 | return 1; |
202 | } |
203 | printf ("Stack address remains the same: %p\n" , old_stack_addr); |
204 | #endif |
205 | |
206 | /* Test that growing the stack region gets new executable pages too. */ |
207 | deeper (f: (void (*) (void)) f); |
208 | |
209 | print_maps (); |
210 | |
211 | #if USE_PTHREADS |
212 | /* Test that a fresh thread now gets an executable stack. */ |
213 | { |
214 | pthread_t th; |
215 | int rc = pthread_create (&th, NULL, &tryme_thread, f); |
216 | if (rc) |
217 | error (1, rc, "pthread_create" ); |
218 | } |
219 | |
220 | puts ("threads go" ); |
221 | /* The existing threads' stacks should have been changed. |
222 | Let them run to test it. */ |
223 | pthread_barrier_wait (&go_barrier); |
224 | |
225 | pthread_exit ((void *) (long int) (! allow_execstack)); |
226 | #endif |
227 | |
228 | return ! allow_execstack; |
229 | } |
230 | |
231 | static void |
232 | deeper (void (*f) (void)) |
233 | { |
234 | char stack[1100 * 1024]; |
235 | explicit_bzero (s: stack, n: sizeof stack); |
236 | (*f) (); |
237 | memfrob (s: stack, n: sizeof stack); |
238 | } |
239 | |
240 | |
241 | #include <support/test-driver.c> |
242 | |