2 * Multithread exerciser - beat up on CSP, TP, and CL from multiple threads.
4 * Written by Doug Mitchell.
7 * Spawn a user-spec'd number of threads, each of which does the following:
9 * testThread(testParams) {
11 * depending on dieValue {
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>
27 #include <Security/Security.h>
33 * As of 3/15/2001, can't link apps which use Security.framework against BSAFE.
35 #define BSAFE_ENABLE 0
38 #define NUM_THREADS 20
40 /* function for both test init and test proper */
41 typedef int (*testFcn
)(TestParams
*testParams
);
51 /* the tests we know about */
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
73 #define RSA_SIGN_ENABLE 1
76 #define RSA_SIGN_ENABLE 0
78 #endif /* BSAFE_ENABLE */
79 #define SSL_THRASH_ENABLE 0
80 #define CSP_RAND_ENABLE 0
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
},
105 { desInit
, desTest
, "desTest", DES_ENABLE
},
106 { rsaSignInit
, rsaSignTest
, "rsaSignTest", RSA_SIGN_ENABLE
}
109 #define NUM_THREAD_TESTS (sizeof(testArray) / sizeof(TestDef))
111 static void usage(char **argv
)
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");
134 /* it happens from time to time on SSL ping */
136 void sigpipe(int sig
)
139 printf("***SIGPIPE***\n");
142 /* common thread-safe routines */
143 static Security::DevRandomGenerator devRand
;
145 CSSM_RETURN
threadGetRandData(
146 const TestParams
*testParams
,
147 CSSM_DATA_PTR data
, // mallocd by caller
148 unsigned numBytes
) // how much to fill
150 devRand
.random(data
->Data
, numBytes
);
151 data
->Length
= numBytes
;
155 /* delay a random amount, 0<delay<10ms */
156 #define MAX_DELAY_US 10000
160 devRand
.random(&usec
, 1);
165 /* in case printf() is malevolently unsafe */
167 static Mutex printLock
;
169 void printChar(char c
)
171 StLock
<Mutex
> _(printLock
);
177 * Optionally start up a CFRunLoop. This is needed to field keychain event callbacks, used
178 * to maintain root cert cache coherency.
181 /* first we need something to register so we *have* a run loop */
182 static OSStatus
kcCacheCallback (
183 SecKeychainEvent keychainEvent
,
184 SecKeychainCallbackInfo
*info
,
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;
193 /* this is the thread which actually runs the CFRunLoop */
194 void *cfRunLoopThread(void *arg
)
196 OSStatus ortn
= SecKeychainAddCallback(kcCacheCallback
,
197 kSecTrustSettingsChangedEventMask
, NULL
);
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? */
203 runLoopInitialized
= 1;
205 /* should not be reached */
206 printf("\n*** Hey! CFRunLoopRun() exited!***\n");
210 static int startCFRunLoop()
212 pthread_t runLoopThread
;
214 int result
= pthread_create(&runLoopThread
, NULL
, cfRunLoopThread
, NULL
);
216 printf("***pthread_create returned %d, aborting\n", result
);
222 /* main pthread body */
223 void *testThread(void *arg
)
225 TestParams
*testParams
= (TestParams
*)arg
;
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
);
234 pthread_exit((void*)status
);
236 return (void *)status
;
240 * Set enables in testArray[]
242 static void setOneEnable(testFcn f
)
245 for(dex
=0; dex
<NUM_THREAD_TESTS
; dex
++) {
246 if(testArray
[dex
].testRun
== f
) {
247 testArray
[dex
].enable
= 1;
251 printf("****setOneEnable: test not found\n");
255 static void setTestEnables(const char *enables
, char **argv
)
257 /* first turn 'em all off */
259 for(dex
=0; dex
<NUM_THREAD_TESTS
; dex
++) {
260 testArray
[dex
].enable
= 0;
263 /* enable specific ones */
264 while(*enables
!= '\0') {
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;
287 case 'b': setOneEnable(rsaSignTest
); break;
288 case 'd': setOneEnable(desTest
); break;
297 int main(int argc
, char **argv
)
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
;
306 pthread_t
*threadList
;
310 TestDef
*thisTestDef
;
311 unsigned numValidTests
;
314 /* user-spec'd parameters */
317 unsigned numThreads
= NUM_THREADS
;
318 unsigned numLoops
= NUM_LOOPS
;
319 char *testOpts
= NULL
;
320 bool abortOnError
= false;
323 for(arg
=1; arg
<argc
; arg
++) {
327 numLoops
= atoi(&argp
[2]);
330 numThreads
= atoi(&argp
[2]);
340 if((argp
[1] != '=') || (argp
[2] == '\0')) {
346 setTestEnables(argp
+ 1, argv
);
363 /* attach to all three modules */
364 cspHand
= cspStartup();
368 clHand
= clStartup();
372 tpHand
= tpStartup();
376 signal(SIGPIPE
, sigpipe
);
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
;
393 thisTest
->progressChar
= '0' + dex
;
397 thisTest
->progressChar
= 'a' + dex
- 10;
400 /* A..Z and if X can run more threads than that, I'll be surprised */
401 thisTest
->progressChar
= 'Z' + dex
- 36;
405 /* Adjust testArray for tests which are actually enabled */
408 for(i
=0; i
<NUM_THREAD_TESTS
; i
++) {
409 if(testArray
[dex
].enable
) {
414 /* delete this one, move remaining tests up */
415 for(j
=dex
; j
<NUM_THREAD_TESTS
-1; j
++) {
416 testArray
[j
] = testArray
[j
+1];
418 /* and re-examine testArray[dex], which we just rewrote */
423 printf("Starting threadTest; args: ");
424 for(i
=1; i
<(unsigned)argc
; i
++) {
425 printf("%s ", argv
[i
]);
430 /* assign a test module to each thread and run its init routine */
431 for(dex
=0; dex
<numThreads
; dex
++) {
433 thisTest
= &testParams
[dex
];
434 thisTest
->testNum
= genRand(0, numValidTests
- 1);
436 thisTestDef
= &testArray
[thisTest
->testNum
];
438 printf("...thread %d: test %s\n", dex
, thisTestDef
->testName
);
440 result
= thisTestDef
->testInit(thisTest
);
442 printf("***Error on %s init; aborting\n", thisTestDef
->testName
);
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
]);
455 printf("***pthread_create returned %d, aborting\n", result
);
461 /* wait for each thread to complete */
462 for(dex
=0; dex
<numThreads
; dex
++) {
464 result
= pthread_join(threadList
[dex
], &status
);
466 printf("***pthread_join returned %d, aborting\n", result
);
470 printf("\n...joined thread %d, status %d\n",
471 dex
, status
? 1 : 0);
480 if(errCount
|| !quiet
) {
481 printf("threadTest complete; errCount %d\n", errCount
);
485 CSSM_ModuleDetach(cspHand
);
488 CSSM_ModuleDetach(clHand
);
491 CSSM_ModuleDetach(tpHand
);