]> git.saurik.com Git - apple/security.git/blob - SecurityTests/clxutils/threadTest/threadTest.cpp
Security-57031.10.10.tar.gz
[apple/security.git] / SecurityTests / clxutils / threadTest / threadTest.cpp
1 /*
2 * Multithread exerciser - beat up on CSP, TP, and CL from multiple threads.
3 *
4 * Written by Doug Mitchell.
5 *
6 *
7 * Spawn a user-spec'd number of threads, each of which does the following:
8 *
9 * testThread(testParams) {
10 * roll the dice;
11 * depending on dieValue {
12 * cgVerify test;
13 * cgConstruct test;
14 * sslPing() test;
15 * etc....
16 * }
17 * }
18 */
19 #include <utilLib/common.h>
20 #include <utilLib/cspwrap.h>
21 #include <clAppUtils/clutils.h>
22 #include "testParams.h"
23 #include <security_utilities/threading.h>
24 #include <security_utilities/utilities.h>
25 #include <security_utilities/devrandom.h>
26 #include <pthread.h>
27 #include <Security/Security.h>
28
29 #include <stdio.h>
30 #include <stdlib.h>
31
32 /*
33 * As of 3/15/2001, can't link apps which use Security.framework against BSAFE.
34 */
35 #define BSAFE_ENABLE 0
36
37 #define NUM_LOOPS 100
38 #define NUM_THREADS 20
39
40 /* function for both test init and test proper */
41 typedef int (*testFcn)(TestParams *testParams);
42
43 /* one test */
44 typedef struct {
45 testFcn testInit;
46 testFcn testRun;
47 const char *testName;
48 char enable;
49 } TestDef;
50
51 /* the tests we know about */
52
53 #define CG_CONSTRUCT_ENABLE 1 /* leak free 12/19 */
54 #define CG_VERIFY_ENABLE 1 /* leak free 12/19 */
55 #define SIGN_VFY_ENABLE 1 /* leak free */
56 #define SYM_TEST_ENABLE 1 /* leak free */
57 #define TIME_ENABLE 0 /* normally off */
58 #define SSL_PING_ENABLE 0 /* leak free 12/19 */
59 #define GET_FIELDS_ENABLE 1 /* leak free */
60 #define GET_CACHED_FLDS_ENABLE 1 /* leak free */
61 #define DER_DECODE_ENABLE 0
62 #define ATTACH_ENABLE 1 /* leak free */
63 #define SEC_TRUST_ENABLE 0 /* works but leaks per 3737232 */
64 #define KC_STATUS_ENABLE 0 /* currently fails: see 6368768 */
65 #define DIGEST_CLIENT_ENABLE 1
66 #define MDS_LOOKUP_ENABLE 1 /* leak free */
67 #define CSSM_ERR_STR_ENABLE 0 /* leak free */
68 #define TRUST_SETTINGS_ENABLE 1
69 #define DB_SETTINGS_ENABLE 0 /* not thread safe */
70 #define COPY_ROOTS_ENABLE 1
71
72 #if BSAFE_ENABLE
73 #define RSA_SIGN_ENABLE 1
74 #define DES_ENABLE 1
75 #else
76 #define RSA_SIGN_ENABLE 0
77 #define DES_ENABLE 0
78 #endif /* BSAFE_ENABLE */
79 #define SSL_THRASH_ENABLE 0
80 #define CSP_RAND_ENABLE 0
81
82 /* when adding to this table be sure to update setTestEnables() as well */
83 TestDef testArray[] = {
84 { cgConstructInit, cgConstruct, "cgConstruct", CG_CONSTRUCT_ENABLE },
85 { cgVerifyInit, cgVerify, "cgVerify", CG_VERIFY_ENABLE },
86 { signVerifyInit, signVerify, "signVerify", SIGN_VFY_ENABLE },
87 { symTestInit, symTest, "symTest", SYM_TEST_ENABLE },
88 { timeInit, timeThread, "timeThread", TIME_ENABLE },
89 { sslPingInit, sslPing, "sslPing", SSL_PING_ENABLE },
90 { getFieldsInit, getFields, "getFields", GET_FIELDS_ENABLE },
91 { getCachedFieldsInit, getCachedFields,"getCachedFields",GET_CACHED_FLDS_ENABLE},
92 { attachTestInit, attachTest, "attachTest", ATTACH_ENABLE },
93 { sslThrashInit, sslThrash, "sslThrash", SSL_THRASH_ENABLE },
94 { cspRandInit, cspRand, "cspRand", CSP_RAND_ENABLE },
95 { derDecodeInit, derDecodeTest, "derDecode", DER_DECODE_ENABLE },
96 { secTrustEvalInit, secTrustEval, "secTrustEval", SEC_TRUST_ENABLE },
97 { kcStatusInit, kcStatus, "kcStatus", KC_STATUS_ENABLE },
98 { digestClientInit, digestClient, "digestClient", DIGEST_CLIENT_ENABLE},
99 { mdsLookupInit, mdsLookup, "mdsLookup", MDS_LOOKUP_ENABLE },
100 { cssmErrStrInit, cssmErrStr, "cssmErrStr", CSSM_ERR_STR_ENABLE },
101 { trustSettingsInit, trustSettingsEval, "trustSettingsEval", TRUST_SETTINGS_ENABLE },
102 { dbOpenCloseInit, dbOpenCloseEval, "dbOpenClose", DB_SETTINGS_ENABLE },
103 { copyRootsInit, copyRootsTest, "copyRoots", COPY_ROOTS_ENABLE },
104 #if BSAFE_ENABLE
105 { desInit, desTest, "desTest", DES_ENABLE },
106 { rsaSignInit, rsaSignTest, "rsaSignTest", RSA_SIGN_ENABLE }
107 #endif
108 };
109 #define NUM_THREAD_TESTS (sizeof(testArray) / sizeof(TestDef))
110
111 static void usage(char **argv)
112 {
113 printf("Usage: %s [options]\n", argv[0]);
114 printf("Options:\n");
115 printf(" l=loopCount (default = %d)\n", NUM_LOOPS);
116 printf(" t=threadCount (default = %d)\n", NUM_THREADS);
117 printf(" e[cvsytpfabdFSrDTkmCer] - enable specific tests\n");
118 printf(" c=cgConstruct v=cgVerify s=signVerify y=symTest\n");
119 printf(" t=timeThread p=sslPing f=getFields a=attach\n");
120 printf(" b=bsafeSignVfy d=bsafeDES F=getCachedFields\n");
121 printf(" S=sslThrash r=cspRand D=derDecode T=SecTrustEval\n");
122 printf(" k=kcStatus m=mdsLookup C=digestClient e=cssmErrorStr\n");
123 printf(" R=TrustSetting B=DBOpenClose o=copyRoots\n");
124 printf(" o=test_specific_opts (see source for details)\n");
125 printf(" a(bort on error)\n");
126 printf(" r(un loop)\n");
127 printf(" q(uiet)\n");
128 printf(" v(erbose)\n");
129 printf(" s(ilent)\n");
130 printf(" h(elp)\n");
131 exit(1);
132 }
133
134 /* it happens from time to time on SSL ping */
135 #include <signal.h>
136 void sigpipe(int sig)
137 {
138 fflush(stdin);
139 printf("***SIGPIPE***\n");
140 }
141
142 /* common thread-safe routines */
143 static Security::DevRandomGenerator devRand;
144
145 CSSM_RETURN threadGetRandData(
146 const TestParams *testParams,
147 CSSM_DATA_PTR data, // mallocd by caller
148 unsigned numBytes) // how much to fill
149 {
150 devRand.random(data->Data, numBytes);
151 data->Length = numBytes;
152 return CSSM_OK;
153 }
154
155 /* delay a random amount, 0<delay<10ms */
156 #define MAX_DELAY_US 10000
157 void randomDelay()
158 {
159 unsigned char usec;
160 devRand.random(&usec, 1);
161 usec %= 10000;
162 usleep(usec);
163 }
164
165 /* in case printf() is malevolently unsafe */
166
167 static Mutex printLock;
168
169 void printChar(char c)
170 {
171 StLock<Mutex> _(printLock);
172 printf("%c", c);
173 fflush(stdout);
174 }
175
176 /*
177 * Optionally start up a CFRunLoop. This is needed to field keychain event callbacks, used
178 * to maintain root cert cache coherency.
179 */
180
181 /* first we need something to register so we *have* a run loop */
182 static OSStatus kcCacheCallback (
183 SecKeychainEvent keychainEvent,
184 SecKeychainCallbackInfo *info,
185 void *context)
186 {
187 return noErr;
188 }
189
190 /* main thread has to wait for this to be set to know a run loop has been set up */
191 static int runLoopInitialized = 0;
192
193 /* this is the thread which actually runs the CFRunLoop */
194 void *cfRunLoopThread(void *arg)
195 {
196 OSStatus ortn = SecKeychainAddCallback(kcCacheCallback,
197 kSecTrustSettingsChangedEventMask, NULL);
198 if(ortn) {
199 printf("registerCacheCallbacks: SecKeychainAddCallback returned %d", (int32_t)ortn);
200 /* Not sure how this could ever happen - maybe if there is no run loop active? */
201 return NULL;
202 }
203 runLoopInitialized = 1;
204 CFRunLoopRun();
205 /* should not be reached */
206 printf("\n*** Hey! CFRunLoopRun() exited!***\n");
207 return NULL;
208 }
209
210 static int startCFRunLoop()
211 {
212 pthread_t runLoopThread;
213
214 int result = pthread_create(&runLoopThread, NULL, cfRunLoopThread, NULL);
215 if(result) {
216 printf("***pthread_create returned %d, aborting\n", result);
217 return -1;
218 }
219 return 0;
220 }
221
222 /* main pthread body */
223 void *testThread(void *arg)
224 {
225 TestParams *testParams = (TestParams *)arg;
226 int status;
227
228 TestDef *thisTestDef = &testArray[testParams->testNum];
229 status = thisTestDef->testRun(testParams);
230 if(!testParams->quiet) {
231 printf("\n...thread %d test %s exiting with status %d\n",
232 testParams->threadNum, thisTestDef->testName, status);
233 }
234 pthread_exit((void*)status);
235 /* NOT REACHED */
236 return (void *)status;
237 }
238
239 /*
240 * Set enables in testArray[]
241 */
242 static void setOneEnable(testFcn f)
243 {
244 unsigned dex;
245 for(dex=0; dex<NUM_THREAD_TESTS; dex++) {
246 if(testArray[dex].testRun == f) {
247 testArray[dex].enable = 1;
248 return;
249 }
250 }
251 printf("****setOneEnable: test not found\n");
252 exit(1);
253 }
254
255 static void setTestEnables(const char *enables, char **argv)
256 {
257 /* first turn 'em all off */
258 unsigned dex;
259 for(dex=0; dex<NUM_THREAD_TESTS; dex++) {
260 testArray[dex].enable = 0;
261 }
262
263 /* enable specific ones */
264 while(*enables != '\0') {
265 switch(*enables) {
266 case 'c': setOneEnable(cgConstruct); break;
267 case 'v': setOneEnable(cgVerify); break;
268 case 's': setOneEnable(signVerify); break;
269 case 'y': setOneEnable(symTest); break;
270 case 't': setOneEnable(timeThread); break;
271 case 'p': setOneEnable(sslPing); break;
272 case 'f': setOneEnable(getFields); break;
273 case 'F': setOneEnable(getCachedFields); break;
274 case 'a': setOneEnable(attachTest); break;
275 case 'S': setOneEnable(sslThrash); break;
276 case 'r': setOneEnable(cspRand); break;
277 case 'D': setOneEnable(derDecodeTest); break;
278 case 'T': setOneEnable(secTrustEval); break;
279 case 'k': setOneEnable(kcStatus); break;
280 case 'C': setOneEnable(digestClient); break;
281 case 'm': setOneEnable(mdsLookup); break;
282 case 'e': setOneEnable(cssmErrStr); break;
283 case 'R': setOneEnable(trustSettingsEval); break;
284 case 'B': setOneEnable(dbOpenCloseEval); break;
285 case 'o': setOneEnable(copyRootsTest); break;
286 #if BSAFE_ENABLE
287 case 'b': setOneEnable(rsaSignTest); break;
288 case 'd': setOneEnable(desTest); break;
289 #endif
290 default:
291 usage(argv);
292 }
293 enables++;
294 }
295 }
296
297 int main(int argc, char **argv)
298 {
299 CSSM_CSP_HANDLE cspHand = 0;
300 CSSM_CL_HANDLE clHand = 0;
301 CSSM_TP_HANDLE tpHand = 0;
302 unsigned errCount = 0;
303 TestParams *testParams;
304 TestParams *thisTest;
305 unsigned dex;
306 pthread_t *threadList;
307 int arg;
308 char *argp;
309 int result;
310 TestDef *thisTestDef;
311 unsigned numValidTests;
312 unsigned i,j;
313
314 /* user-spec'd parameters */
315 char quiet = 0;
316 char verbose = 0;
317 unsigned numThreads = NUM_THREADS;
318 unsigned numLoops = NUM_LOOPS;
319 char *testOpts = NULL;
320 bool abortOnError = false;
321 bool silent = false;
322
323 for(arg=1; arg<argc; arg++) {
324 argp = argv[arg];
325 switch(argp[0]) {
326 case 'l':
327 numLoops = atoi(&argp[2]);
328 break;
329 case 't':
330 numThreads = atoi(&argp[2]);
331 break;
332 break;
333 case 'q':
334 quiet = 1;
335 break;
336 case 'v':
337 verbose = 1;
338 break;
339 case 'o':
340 if((argp[1] != '=') || (argp[2] == '\0')) {
341 usage(argv);
342 }
343 testOpts = argp + 2;
344 break;
345 case 'e':
346 setTestEnables(argp + 1, argv);
347 break;
348 case 'a':
349 abortOnError = true;
350 break;
351 case 'r':
352 startCFRunLoop();
353 break;
354 case 's':
355 silent = true;
356 quiet = 1;
357 break;
358 default:
359 usage(argv);
360 }
361 }
362
363 /* attach to all three modules */
364 cspHand = cspStartup();
365 if(cspHand == 0) {
366 exit(1);
367 }
368 clHand = clStartup();
369 if(clHand == 0) {
370 goto abort;
371 }
372 tpHand = tpStartup();
373 if(tpHand == 0) {
374 goto abort;
375 }
376 signal(SIGPIPE, sigpipe);
377
378 /* malloc and init TestParams for all requested threads */
379 testParams = (TestParams *)malloc(numThreads * sizeof(TestParams));
380 for(dex=0; dex<numThreads; dex++) {
381 thisTest = &testParams[dex];
382 thisTest->numLoops = numLoops;
383 thisTest->verbose = verbose;
384 thisTest->quiet = quiet;
385 thisTest->threadNum = dex;
386 thisTest->cspHand = cspHand;
387 thisTest->clHand = clHand;
388 thisTest->tpHand = tpHand;
389 thisTest->testOpts = testOpts;
390
391 if(dex < 10) {
392 /* 0..9 */
393 thisTest->progressChar = '0' + dex;
394 }
395 else if(dex < 36) {
396 /* a..z */
397 thisTest->progressChar = 'a' + dex - 10;
398 }
399 else {
400 /* A..Z and if X can run more threads than that, I'll be surprised */
401 thisTest->progressChar = 'Z' + dex - 36;
402 }
403 }
404
405 /* Adjust testArray for tests which are actually enabled */
406 numValidTests = 0;
407 dex=0;
408 for(i=0; i<NUM_THREAD_TESTS; i++) {
409 if(testArray[dex].enable) {
410 numValidTests++;
411 dex++;
412 }
413 else {
414 /* delete this one, move remaining tests up */
415 for(j=dex; j<NUM_THREAD_TESTS-1; j++) {
416 testArray[j] = testArray[j+1];
417 }
418 /* and re-examine testArray[dex], which we just rewrote */
419 }
420 }
421
422 if(!silent) {
423 printf("Starting threadTest; args: ");
424 for(i=1; i<(unsigned)argc; i++) {
425 printf("%s ", argv[i]);
426 }
427 printf("\n");
428 }
429
430 /* assign a test module to each thread and run its init routine */
431 for(dex=0; dex<numThreads; dex++) {
432 /* roll the dice */
433 thisTest = &testParams[dex];
434 thisTest->testNum = genRand(0, numValidTests - 1);
435
436 thisTestDef = &testArray[thisTest->testNum];
437 if(!quiet) {
438 printf("...thread %d: test %s\n", dex, thisTestDef->testName);
439 }
440 result = thisTestDef->testInit(thisTest);
441 if(result) {
442 printf("***Error on %s init; aborting\n", thisTestDef->testName);
443 errCount++;
444 goto abort;
445 }
446 }
447
448
449 /* start up each thread */
450 threadList = (pthread_t *)malloc(numThreads * sizeof(pthread_t));
451 for(dex=0; dex<numThreads; dex++) {
452 int result = pthread_create(&threadList[dex], NULL,
453 testThread, &testParams[dex]);
454 if(result) {
455 printf("***pthread_create returned %d, aborting\n", result);
456 errCount++;
457 goto abort;
458 }
459 }
460
461 /* wait for each thread to complete */
462 for(dex=0; dex<numThreads; dex++) {
463 void *status;
464 result = pthread_join(threadList[dex], &status);
465 if(result) {
466 printf("***pthread_join returned %d, aborting\n", result);
467 goto abort;
468 }
469 if(!quiet) {
470 printf("\n...joined thread %d, status %d\n",
471 dex, status ? 1 : 0);
472 }
473 if(status != NULL) {
474 errCount++;
475 if(abortOnError) {
476 break;
477 }
478 }
479 }
480 if(errCount || !quiet) {
481 printf("threadTest complete; errCount %d\n", errCount);
482 }
483 abort:
484 if(cspHand != 0) {
485 CSSM_ModuleDetach(cspHand);
486 }
487 if(clHand != 0) {
488 CSSM_ModuleDetach(clHand);
489 }
490 if(tpHand != 0) {
491 CSSM_ModuleDetach(tpHand);
492 }
493 return errCount;
494 }
495
496