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