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 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 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 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 138 static void fail(void) 139 { 140 /* Shouldn't get here */ 141 err(5, 1); 142 } 143 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 171 void func2(void) 172 { 173 if (swapcontext(&ctx[2], &ctx[1]) != 0) err(3, 1); 174 entered_func2++; 175 } 176 177 void just_exit(void) 178 { 179 if (errct == 0) printf("ok\n"); 180 _exit(1); 181 } 182 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 194 void verify_main_reenter(void) 195 { 196 if (reentered_main == 0) err(4, 1); 197 } 198 199 int set_context_test_value; 200 void set_context_test_thread1(void){ 201 set_context_test_value |= 0x1; 202 setcontext(&ctx[2]); 203 err(1, 24); 204 205 } 206 207 void set_context_test_thread2(void){ 208 set_context_test_value |= 0x1 << 1; 209 setcontext(&ctx[0]); 210 err(1, 23); 211 } 212 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