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
print_maps(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 *
tryme_thread(void * f)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 *
waiter_thread(void * arg)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
do_test(void)54 do_test (void)
55 {
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 
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 ("tst-execstack-mod.so", RTLD_LAZY);
141   if (h == NULL)
142     {
143       printf ("cannot load: %s\n", dlerror ());
144       return allow_execstack;
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 
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 ((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
deeper(void (* f)(void))227 deeper (void (*f) (void))
228 {
229   char stack[1100 * 1024];
230   explicit_bzero (stack, sizeof stack);
231   (*f) ();
232   memfrob (stack, sizeof stack);
233 }
234 
235 
236 #include <support/test-driver.c>
237