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 */ 41typedef int (*testFcn)(TestParams *testParams); 42 43/* one test */ 44typedef 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 */ 83TestDef 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 111static 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> 136void sigpipe(int sig) 137{ 138 fflush(stdin); 139 printf("***SIGPIPE***\n"); 140} 141 142/* common thread-safe routines */ 143static Security::DevRandomGenerator devRand; 144 145CSSM_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 157void 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 167static Mutex printLock; 168 169void 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 */ 182static 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 */ 191static int runLoopInitialized = 0; 192 193/* this is the thread which actually runs the CFRunLoop */ 194void *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 210static 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 */ 223void *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 */ 242static 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 255static 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 297int 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 } 483abort: 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 497