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
12static void
13print_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
23static void deeper (void (*f) (void));
24
25#if USE_PTHREADS
26# include <pthread.h>
27
28static void *
29tryme_thread (void *f)
30{
31 (*((void (*) (void)) f)) ();
32
33 return 0;
34}
35
36static pthread_barrier_t startup_barrier, go_barrier;
37static void *
38waiter_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
50static bool allow_execstack = true;
51
52
53static int
54do_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
231static void
232deeper (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

source code of glibc/elf/tst-execstack.c