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 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
226static void
227deeper (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

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