1 /* Test51.c
2 *
3 * Test getcontext, setcontext, makecontext, and swapcontext system calls.
4 *
5 * Part of this test is somewhat based on the GNU GCC ucontext test set.
6 *
7 */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <errno.h>
13 #include <string.h>
14 #include <ucontext.h>
15 #include <math.h>
16 #include <fenv.h>
17
18 #include <sys/signal.h>
19
20 /* We can't use a global subtest variable reliably, because threads might
21 change the value when we reenter a thread (i.e., report wrong subtest
22 number). */
23 #define err(st, en) do { subtest = st; e(en); } while(0)
24
25 void do_calcs(void);
26 void do_child(void);
27 void do_parent(void);
28 void func1(int a, int b, int c, int d, int e, int f, int g, int h, int
29 i, int j, int k, int l, int m, int n, int o, int p, int q, int r, int s,
30 int t, int u, int v, int w, int x, int y, int z, int aa, int bb);
31 void func2(void);
32 void just_exit(void);
33 void test_brk(void);
34 void verify_main_reenter(void);
35
36 int max_error = 5;
37 #include "common.h"
38
39 #define SSIZE 32768
40 #define ROUNDS 10
41 #define SWAPS 10
42
43
44 int subtest;
45 ucontext_t ctx[3];
46 int entered_func1, entered_func2, reentered_main, entered_overflow;
47
48 static char st_stack[SSIZE];
49 static volatile int shift, global;
50
do_calcs(void)51 void do_calcs(void)
52 {
53 float a, b, c, d, e;
54 float foo, bar;
55 int i;
56
57 a = 1.1;
58 b = 2.2;
59 c = 3.3;
60 d = 4.4;
61 e = 5.5;
62
63 foo = a * b; /* 2.42 */
64 bar = c * d; /* 14.52 */
65
66 i = 0;
67 while(i < ROUNDS) {
68 foo += c; /* 5.72 */
69 foo *= d; /* 25.168 */
70 foo /= e; /* 4.5760 */
71 bar -= a; /* 13.42 */
72 bar *= b; /* 29.524 */
73 bar /= e; /* 5.3680 */
74
75 /* Undo */
76 foo *= e;
77 foo /= d;
78 foo -= c;
79
80 bar *= e;
81 bar /= b;
82 bar += a;
83
84 i++;
85 }
86 if(fabs(foo - (a * b)) > 0.0001) err(8, 1);
87 if(fabs(bar - (c * d)) > 0.0001) err(8, 2);
88 }
89
do_child(void)90 void do_child(void)
91 {
92 int s;
93 s = 1;
94
95 /* Initialize FPU context and verify it's set to round to nearest. */
96 if (fegetround() != FE_TONEAREST) err(9, 1);
97
98 /* Now we change the rounding to something else, and this should be preserved
99 between context swaps. */
100 if (fesetround(FE_DOWNWARD) != 0) err(9, 2);
101
102 while(s < SWAPS) {
103 s++;
104 if (swapcontext(&ctx[2], &ctx[1]) == -1) err(9, 2);
105 do_calcs();
106 if (fegetround() != FE_DOWNWARD) err(9, 4);
107 }
108 quit();
109 }
110
do_parent(void)111 void do_parent(void)
112 {
113 ucontext_t dummy;
114 int s;
115 s = 1;
116
117 /* Initialize FPU context and verify it's set to round to nearest. */
118 if (fegetround() != FE_TONEAREST) err(10, 1);
119
120 /* Now we change the rounding to something else, and this should be preserved
121 between context swaps. */
122 if (fesetround(FE_UPWARD) != 0) err(10, 2);
123
124 /* Quick check to make sure that getcontext does not reset the FPU state. */
125 getcontext(&dummy);
126
127 if (fegetround() != FE_UPWARD) err(10, 3);
128
129 while(s < SWAPS) {
130 do_calcs();
131 if (fegetround() != FE_UPWARD) err(10, 4);
132 s++;
133 if (swapcontext(&ctx[1], &ctx[2]) == -1) err(10, 5);
134 }
135 /* Returning to main thread through uc_link */
136 }
137
fail(void)138 static void fail(void)
139 {
140 /* Shouldn't get here */
141 err(5, 1);
142 }
143
func1(int a,int b,int c,int d,int e,int f,int g,int h,int i,int j,int k,int l,int m,int n,int o,int p,int q,int r,int s,int t,int u,int v,int w,int x,int y,int z,int aa,int bb)144 void func1(int a, int b, int c, int d, int e, int f, int g,
145 int h, int i, int j, int k, int l, int m, int n,
146 int o, int p, int q, int r, int s, int t, int u,
147 int v, int w, int x, int y, int z, int aa, int bb)
148 {
149 if ( a != (0x0000001 << shift) || b != (0x0000004 << shift) ||
150 c != (0x0000010 << shift) || d != (0x0000040 << shift) ||
151 e != (0x0000100 << shift) || f != (0x0000400 << shift) ||
152 g != (0x0001000 << shift) || h != (0x0004000 << shift) ||
153 i != (0x0010000 << shift) || j != (0x0040000 << shift) ||
154 k != (0x0100000 << shift) || l != (0x0400000 << shift) ||
155 m != (0x1000000 << shift) || n != (0x4000000 << shift) ||
156 o != (0x0000002 << shift) || p != (0x0000008 << shift) ||
157 q != (0x0000020 << shift) || r != (0x0000080 << shift) ||
158 s != (0x0000200 << shift) || t != (0x0000800 << shift) ||
159 u != (0x0002000 << shift) || v != (0x0008000 << shift) ||
160 w != (0x0020000 << shift) || x != (0x0080000 << shift) ||
161 y != (0x0200000 << shift) || z != (0x0800000 << shift) ||
162 aa != (0x2000000 << shift) || bb != (0x8000000 << shift) ) {
163 err(2, 1);
164 }
165
166 if (shift && swapcontext (&ctx[1], &ctx[2]) != 0) err(2, 2);
167 shift++;
168 entered_func1++;
169 }
170
func2(void)171 void func2(void)
172 {
173 if (swapcontext(&ctx[2], &ctx[1]) != 0) err(3, 1);
174 entered_func2++;
175 }
176
just_exit(void)177 void just_exit(void)
178 {
179 if (errct == 0) printf("ok\n");
180 _exit(1);
181 }
182
test_brk(void)183 void test_brk(void)
184 {
185 char *big_stack;
186
187 big_stack = malloc(16 * 1024 * 1024); /* 16 MB */
188 /* If this fails, it is likely brk system call failed due stack/data segments
189 collision detection. Unless the system really is low on memory, this is an
190 error. */
191 if (big_stack == NULL) err(7, 1);
192 }
193
verify_main_reenter(void)194 void verify_main_reenter(void)
195 {
196 if (reentered_main == 0) err(4, 1);
197 }
198
199 int set_context_test_value;
set_context_test_thread1(void)200 static void set_context_test_thread1(void){
201 set_context_test_value |= 0x1;
202 setcontext(&ctx[2]);
203 err(1, 24);
204
205 }
206
set_context_test_thread2(void)207 static void set_context_test_thread2(void){
208 set_context_test_value |= 0x1 << 1;
209 setcontext(&ctx[0]);
210 err(1, 23);
211 }
212
main(void)213 int main(void)
214 {
215 start(51);
216
217 atexit(verify_main_reenter);
218
219 /* Save current context in ctx[0] */
220 if (getcontext(&ctx[0]) != 0) {
221 /* Don't verify reentering main, not going to happen */
222 atexit(just_exit);
223 err(1, 1);
224 }
225
226 ctx[1] = ctx[0];
227 ctx[1].uc_stack.ss_sp = st_stack;
228 ctx[1].uc_stack.ss_size = SSIZE;
229 ctx[1].uc_link = &ctx[0]; /* When done running, return here */
230
231 /* ctx[1] is going to run func1 and then return here (uc_link). */
232 /* We'll see later on whether makecontext worked. */
233 makecontext(&ctx[1], (void (*) (void)) func1, 28,
234 (0x0000001 << shift), (0x0000004 << shift),
235 (0x0000010 << shift), (0x0000040 << shift),
236 (0x0000100 << shift), (0x0000400 << shift),
237 (0x0001000 << shift), (0x0004000 << shift),
238 (0x0010000 << shift), (0x0040000 << shift),
239 (0x0100000 << shift), (0x0400000 << shift),
240 (0x1000000 << shift), (0x4000000 << shift),
241 (0x0000002 << shift), (0x0000008 << shift),
242 (0x0000020 << shift), (0x0000080 << shift),
243 (0x0000200 << shift), (0x0000800 << shift),
244 (0x0002000 << shift), (0x0008000 << shift),
245 (0x0020000 << shift), (0x0080000 << shift),
246 (0x0200000 << shift), (0x0800000 << shift),
247 (0x2000000 << shift), (0x8000000 << shift));
248
249 if (++global == 1) {
250 /* First time we're here. Let's run ctx[1] and return to ctx[0] when
251 * we're done. Note that we return to above the 'makecontext' call. */
252 if (setcontext(&ctx[1]) != 0) err(1, 2);
253 }
254 if (global != 2) {
255 /* When ++global was 1 we let ctx[1] run and returned to ctx[0], so
256 above ++global is executed again and should've become 2. */
257 err(1, 3);
258 }
259
260 /* Setup ctx[2] to run func2 */
261 if (getcontext(&ctx[2]) != 0) err(1, 4);
262 ctx[2].uc_stack.ss_sp = malloc(SSIZE);
263 ctx[2].uc_stack.ss_size = SSIZE;
264 ctx[2].uc_link = &ctx[1];
265 makecontext(&ctx[2], (void (*) (void)) func2, 0);
266
267 /* Now things become tricky. ctx[2] is set up such that when it finishes
268 running, and starts ctx[1] again. However, func1 swaps back to func2. Then,
269 when func2 has finished running, we continue with ctx[1] and, finally, we
270 return to ctx[0]. */
271 if (swapcontext(&ctx[0], &ctx[2]) != 0) err(1, 5); /* makecontext failed? */
272 reentered_main = 1;
273
274 /* The call graph is as follows:
275 *
276 * ########
277 * /--------># main #
278 * 7 /----########----\
279 * | | ^ |
280 * | 1 2 3
281 * | V | V
282 * #########----/ #########
283 * # func1 #<-------4-------# func2 #
284 * #########--------5------>#########
285 * ^ |
286 * | |
287 * \---------6--------/
288 *
289 * Main calls func1, func1 increases entered_func1, and returns to main. Main
290 * calls func2, swaps to func1, swaps to func2, which increases entered_func2,
291 * continues with func1, which increases entered_func1 again, continues to
292 * main, where reentered_main is set to 1. In effect, entered_func1 == 2,
293 * entered_func2 == 1, reentered_main == 1. Verify that. */
294
295 if (entered_func1 != 2) err(1, 6);
296 if (entered_func2 != 1) err(1, 7);
297 /* reentered_main == 1 is verified upon exit */
298
299 /* Try to allocate too small a stack */
300 free(ctx[2].uc_stack.ss_sp); /* Deallocate stack space first */
301 if (getcontext(&ctx[2]) != 0) err(1, 8);
302 ctx[2].uc_stack.ss_sp = malloc(MINSIGSTKSZ-1);
303 ctx[2].uc_stack.ss_size = MINSIGSTKSZ-1;
304 ctx[2].uc_link = &ctx[0];
305 makecontext(&ctx[2], (void (*) (void)) fail, 0);
306 /* Because makecontext is void, we can only detect an error by trying to use
307 the invalid context */
308 if (swapcontext(&ctx[0], &ctx[2]) == 0) err(1, 9);
309
310 /* Try to allocate a huge stack to force the usage of brk/sbrk system call
311 to enlarge the data segment. Because we are fiddling with the stack
312 pointer, the OS might think the stack segment and data segment have
313 collided and kill us. This is wrong and therefore the following should
314 work. */
315 free(ctx[2].uc_stack.ss_sp); /* Deallocate stack space first */
316 if (getcontext(&ctx[2]) != 0) err(1, 14);
317 ctx[2].uc_stack.ss_sp = malloc(8 * 1024 * 1024); /* 8 MB */
318 ctx[2].uc_stack.ss_size = 8 * 1024 * 1024;
319 ctx[2].uc_link = &ctx[0];
320 makecontext(&ctx[2], (void (*) (void)) test_brk, 0);
321 if (swapcontext(&ctx[0], &ctx[2]) != 0) err(1, 15);
322
323 ctx[1].uc_link = &ctx[0];
324 ctx[2].uc_link = NULL;
325 makecontext(&ctx[1], (void (*) (void)) do_parent, 0);
326 makecontext(&ctx[2], (void (*) (void)) do_child, 0);
327 if (swapcontext(&ctx[0], &ctx[2]) == -1) err(1, 16);
328
329 /* test setcontext first do some cleanup */
330
331 free(ctx[1].uc_stack.ss_sp);
332 free(ctx[2].uc_stack.ss_sp);
333
334 memset(&ctx[0],0,sizeof(ucontext_t));
335 memset(&ctx[1],0,sizeof(ucontext_t));
336 memset(&ctx[2],0,sizeof(ucontext_t));
337
338
339 /* create 3 new contexts */
340 volatile int cb =1;
341
342 /* control will be returned here */
343 set_context_test_value = 0;
344 if (getcontext(&ctx[0]) != 0) err(1, 17);
345 if (set_context_test_value != 0x3) err(1, 20);
346 if (cb == 1) {
347 cb =0;
348 if (getcontext(&ctx[1]) != 0) err(1, 18);
349 if (getcontext(&ctx[2]) != 0) err(1, 19);
350
351 // allocate new stacks */
352 ctx[1].uc_stack.ss_sp = malloc(SSIZE);
353 ctx[1].uc_stack.ss_size = SSIZE;
354 ctx[2].uc_stack.ss_sp = malloc(SSIZE);
355 ctx[2].uc_stack.ss_size = SSIZE;
356
357 makecontext(&ctx[1],set_context_test_thread1,0);
358 makecontext(&ctx[2],set_context_test_thread2,0);
359 if( setcontext(&ctx[1]) != 0){
360 err(1, 21);
361 }
362 err(1, 22);
363 }
364
365 quit();
366 return(-1);
367 }
368
369