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 | void *h = dlopen (file: "tst-execstack-mod.so" , RTLD_LAZY); |
141 | if (h == NULL) |
142 | { |
143 | printf (format: "cannot load: %s\n" , dlerror ()); |
144 | return allow_execstack; |
145 | } |
146 | |
147 | f = dlsym (handle: h, name: "tryme" ); |
148 | if (f == NULL) |
149 | { |
150 | printf (format: "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 | |
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 | |
201 | /* Test that growing the stack region gets new executable pages too. */ |
202 | deeper (f: (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 | |
220 | pthread_exit ((void *) (long int) (! allow_execstack)); |
221 | #endif |
222 | |
223 | return ! allow_execstack; |
224 | } |
225 | |
226 | static void |
227 | deeper (void (*f) (void)) |
228 | { |
229 | char stack[1100 * 1024]; |
230 | explicit_bzero (s: stack, n: sizeof stack); |
231 | (*f) (); |
232 | memfrob (s: stack, n: sizeof stack); |
233 | } |
234 | |
235 | |
236 | #include <support/test-driver.c> |
237 | |