]> git.saurik.com Git - apple/security.git/blobdiff - SecurityTests/clxutils/threadTest/threadTest.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / SecurityTests / clxutils / threadTest / threadTest.cpp
diff --git a/SecurityTests/clxutils/threadTest/threadTest.cpp b/SecurityTests/clxutils/threadTest/threadTest.cpp
new file mode 100644 (file)
index 0000000..563ff39
--- /dev/null
@@ -0,0 +1,496 @@
+/*
+ * Multithread exerciser - beat up on CSP, TP, and CL from multiple threads.
+ *
+ * Written by Doug Mitchell. 
+ *
+ *
+ * Spawn a user-spec'd number of threads, each of which does the following:
+ *
+ * testThread(testParams) {
+ *    roll the dice;
+ *    depending on dieValue {
+ *             cgVerify test;
+ *             cgConstruct test;
+ *      sslPing() test;
+ *             etc....
+ *    }
+ * }
+ */
+#include <utilLib/common.h>
+#include <utilLib/cspwrap.h>   
+#include <clAppUtils/clutils.h>
+#include "testParams.h"
+#include <security_utilities/threading.h>
+#include <security_utilities/utilities.h>
+#include <security_utilities/devrandom.h>
+#include <pthread.h>
+#include <Security/Security.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/*
+ * As of 3/15/2001, can't link apps which use Security.framework against BSAFE.
+ */
+#define BSAFE_ENABLE   0
+
+#define NUM_LOOPS              100
+#define NUM_THREADS            20
+
+/* function for both test init and test proper */
+typedef int (*testFcn)(TestParams *testParams);
+
+/* one test */
+typedef struct {
+       testFcn         testInit;
+       testFcn         testRun;
+       const char      *testName;
+       char            enable;
+} TestDef;
+
+/* the tests we know about */
+
+#define CG_CONSTRUCT_ENABLE            1               /* leak free 12/19 */
+#define CG_VERIFY_ENABLE               1               /* leak free 12/19 */
+#define SIGN_VFY_ENABLE                        1               /* leak free */
+#define SYM_TEST_ENABLE                        1               /* leak free */
+#define TIME_ENABLE                            0               /* normally off */
+#define SSL_PING_ENABLE                        0               /* leak free 12/19 */
+#define GET_FIELDS_ENABLE              1               /* leak free */
+#define GET_CACHED_FLDS_ENABLE 1               /* leak free */
+#define DER_DECODE_ENABLE              0
+#define ATTACH_ENABLE                  1               /* leak free */
+#define SEC_TRUST_ENABLE               0               /* works but leaks per 3737232 */
+#define KC_STATUS_ENABLE               0               /* currently fails: see 6368768 */
+#define DIGEST_CLIENT_ENABLE   1
+#define MDS_LOOKUP_ENABLE              1               /* leak free */
+#define CSSM_ERR_STR_ENABLE            0               /* leak free */
+#define TRUST_SETTINGS_ENABLE  1
+#define DB_SETTINGS_ENABLE             0               /* not thread safe */
+#define COPY_ROOTS_ENABLE       1
+
+#if            BSAFE_ENABLE
+#define RSA_SIGN_ENABLE                        1
+#define DES_ENABLE                             1
+#else
+#define RSA_SIGN_ENABLE                        0
+#define DES_ENABLE                             0
+#endif /* BSAFE_ENABLE */
+#define SSL_THRASH_ENABLE              0
+#define CSP_RAND_ENABLE                        0
+
+/* when adding to this table be sure to update setTestEnables() as well */
+TestDef testArray[] = {
+       { cgConstructInit,              cgConstruct,    "cgConstruct",  CG_CONSTRUCT_ENABLE },
+       { cgVerifyInit,                 cgVerify,               "cgVerify",     CG_VERIFY_ENABLE        },
+       { signVerifyInit,               signVerify,             "signVerify",   SIGN_VFY_ENABLE         },
+       { symTestInit,                  symTest,                "symTest",              SYM_TEST_ENABLE         },
+       { timeInit,                     timeThread,             "timeThread",   TIME_ENABLE             },
+       { sslPingInit,                  sslPing,                "sslPing",              SSL_PING_ENABLE         },
+       { getFieldsInit,                getFields,              "getFields",    GET_FIELDS_ENABLE       },
+       { getCachedFieldsInit,  getCachedFields,"getCachedFields",GET_CACHED_FLDS_ENABLE},
+       { attachTestInit,               attachTest,             "attachTest",   ATTACH_ENABLE           },
+       { sslThrashInit,                sslThrash,              "sslThrash",    SSL_THRASH_ENABLE       },
+       { cspRandInit,                  cspRand,                "cspRand",              CSP_RAND_ENABLE         },
+       { derDecodeInit,                derDecodeTest,  "derDecode",    DER_DECODE_ENABLE       },
+       { secTrustEvalInit,             secTrustEval,   "secTrustEval", SEC_TRUST_ENABLE        },
+       { kcStatusInit,                 kcStatus,               "kcStatus",             KC_STATUS_ENABLE        },
+       { digestClientInit,             digestClient,   "digestClient", DIGEST_CLIENT_ENABLE},
+       { mdsLookupInit,                mdsLookup,              "mdsLookup",    MDS_LOOKUP_ENABLE       },
+       { cssmErrStrInit,               cssmErrStr,             "cssmErrStr",   CSSM_ERR_STR_ENABLE     },
+       { trustSettingsInit,    trustSettingsEval, "trustSettingsEval", TRUST_SETTINGS_ENABLE },
+       { dbOpenCloseInit,              dbOpenCloseEval, "dbOpenClose", DB_SETTINGS_ENABLE  },
+    { copyRootsInit,        copyRootsTest,  "copyRoots",    COPY_ROOTS_ENABLE   },
+       #if     BSAFE_ENABLE
+       { desInit,                              desTest,                "desTest",              DES_ENABLE                      },
+       { rsaSignInit,                  rsaSignTest,    "rsaSignTest",  RSA_SIGN_ENABLE         }
+       #endif
+};
+#define NUM_THREAD_TESTS       (sizeof(testArray) / sizeof(TestDef))
+
+static void usage(char **argv)
+{
+    printf("Usage: %s [options]\n", argv[0]);
+    printf("Options:\n");
+    printf("   l=loopCount (default = %d)\n", NUM_LOOPS);
+       printf("   t=threadCount (default = %d)\n", NUM_THREADS);
+       printf("   e[cvsytpfabdFSrDTkmCer] - enable specific tests\n");
+       printf("       c=cgConstruct    v=cgVerify    s=signVerify   y=symTest\n");
+       printf("       t=timeThread     p=sslPing     f=getFields    a=attach\n");
+       printf("       b=bsafeSignVfy   d=bsafeDES    F=getCachedFields\n");
+       printf("       S=sslThrash      r=cspRand     D=derDecode    T=SecTrustEval\n");
+       printf("       k=kcStatus       m=mdsLookup   C=digestClient e=cssmErrorStr\n");
+       printf("       R=TrustSetting   B=DBOpenClose o=copyRoots\n");
+       printf("   o=test_specific_opts (see source for details)\n");
+       printf("   a(bort on error)\n");
+       printf("   r(un loop)\n");
+       printf("   q(uiet)\n");
+       printf("   v(erbose)\n");
+       printf("   s(ilent)\n");
+       printf("   h(elp)\n");
+    exit(1);
+}
+
+/* it happens from time to time on SSL ping */
+#include <signal.h>
+void sigpipe(int sig) 
+{ 
+       fflush(stdin);
+       printf("***SIGPIPE***\n");
+}
+
+/* common thread-safe routines */
+static Security::DevRandomGenerator devRand;
+
+CSSM_RETURN threadGetRandData(
+       const TestParams        *testParams,
+       CSSM_DATA_PTR           data,           // mallocd by caller
+       unsigned                        numBytes)       // how much to fill
+{
+       devRand.random(data->Data, numBytes);
+       data->Length = numBytes;
+       return CSSM_OK;
+}
+
+/* delay a random amount, 0<delay<10ms */
+#define MAX_DELAY_US   10000
+void randomDelay()
+{
+       unsigned char usec;
+       devRand.random(&usec, 1);
+       usec %= 10000;
+       usleep(usec);
+}
+
+/* in case printf() is malevolently unsafe */
+
+static Mutex printLock;
+
+void printChar(char c)
+{
+       StLock<Mutex> _(printLock);
+       printf("%c", c);
+       fflush(stdout);
+}
+
+/* 
+ * Optionally start up a CFRunLoop. This is needed to field keychain event callbacks, used
+ * to maintain root cert cache coherency. 
+ */
+/* first we need something to register so we *have* a run loop */
+static OSStatus kcCacheCallback (
+   SecKeychainEvent keychainEvent,
+   SecKeychainCallbackInfo *info,
+   void *context)
+{
+       return noErr;
+}
+
+/* main thread has to wait for this to be set to know a run loop has been set up */
+static int runLoopInitialized = 0;
+
+/* this is the thread which actually runs the CFRunLoop */
+void *cfRunLoopThread(void *arg)
+{
+       OSStatus ortn = SecKeychainAddCallback(kcCacheCallback, 
+               kSecTrustSettingsChangedEventMask, NULL);
+       if(ortn) {
+               printf("registerCacheCallbacks: SecKeychainAddCallback returned %d", (int32_t)ortn);
+               /* Not sure how this could ever happen - maybe if there is no run loop active? */
+               return NULL;
+       }
+       runLoopInitialized = 1;
+       CFRunLoopRun();
+       /* should not be reached */
+       printf("\n*** Hey! CFRunLoopRun() exited!***\n");
+       return NULL;
+}
+
+static int startCFRunLoop()
+{
+       pthread_t runLoopThread;
+       
+       int result = pthread_create(&runLoopThread, NULL, cfRunLoopThread, NULL);
+       if(result) {
+               printf("***pthread_create returned %d, aborting\n", result);
+               return -1;
+       }
+       return 0;
+}
+
+/* main pthread body */
+void *testThread(void *arg)
+{
+       TestParams *testParams = (TestParams *)arg;
+       int status;
+       
+       TestDef *thisTestDef = &testArray[testParams->testNum];
+       status = thisTestDef->testRun(testParams);
+       if(!testParams->quiet) {
+               printf("\n...thread %d test %s exiting with status %d\n", 
+                       testParams->threadNum, thisTestDef->testName, status);
+       }
+       pthread_exit((void*)status);
+       /* NOT REACHED */
+       return (void *)status;
+}
+
+/*
+ * Set enables in testArray[] 
+ */
+static void setOneEnable(testFcn f)
+{
+       unsigned dex;
+       for(dex=0; dex<NUM_THREAD_TESTS; dex++) {
+               if(testArray[dex].testRun == f) {
+                       testArray[dex].enable = 1;
+                       return;
+               }
+       }
+       printf("****setOneEnable: test not found\n");
+       exit(1);
+}
+
+static void setTestEnables(const char *enables, char **argv)
+{
+       /* first turn 'em all off */
+       unsigned dex;
+       for(dex=0; dex<NUM_THREAD_TESTS; dex++) {
+               testArray[dex].enable = 0;
+       }
+       
+       /* enable specific ones */
+       while(*enables != '\0') {
+               switch(*enables) {
+                       case 'c':       setOneEnable(cgConstruct); break;
+                       case 'v':       setOneEnable(cgVerify); break;
+                       case 's':       setOneEnable(signVerify); break;
+                       case 'y':       setOneEnable(symTest); break;
+                       case 't':       setOneEnable(timeThread); break;
+                       case 'p':       setOneEnable(sslPing); break;
+                       case 'f':       setOneEnable(getFields); break;
+                       case 'F':       setOneEnable(getCachedFields); break;
+                       case 'a':       setOneEnable(attachTest); break;
+                       case 'S':       setOneEnable(sslThrash); break;
+                       case 'r':       setOneEnable(cspRand); break;
+                       case 'D':       setOneEnable(derDecodeTest); break;
+                       case 'T':       setOneEnable(secTrustEval); break;
+                       case 'k':       setOneEnable(kcStatus); break;
+                       case 'C':       setOneEnable(digestClient); break;
+                       case 'm':       setOneEnable(mdsLookup); break;
+                       case 'e':       setOneEnable(cssmErrStr); break;
+                       case 'R':       setOneEnable(trustSettingsEval); break;
+                       case 'B':   setOneEnable(dbOpenCloseEval); break;
+                       case 'o':   setOneEnable(copyRootsTest); break;
+                       #if             BSAFE_ENABLE
+                       case 'b':       setOneEnable(rsaSignTest); break;
+                       case 'd':       setOneEnable(desTest); break;
+                       #endif
+                       default:
+                               usage(argv);
+               }
+               enables++;
+       }
+}
+
+int main(int argc, char **argv)
+{   
+       CSSM_CSP_HANDLE cspHand = 0;
+       CSSM_CL_HANDLE  clHand = 0;
+       CSSM_TP_HANDLE  tpHand = 0;
+       unsigned                errCount = 0;
+       TestParams              *testParams;
+       TestParams              *thisTest;
+       unsigned                dex;
+       pthread_t               *threadList;
+       int                             arg;
+       char                    *argp;
+       int                             result;
+       TestDef                 *thisTestDef;
+       unsigned                numValidTests;
+       unsigned                i,j;
+       
+       /* user-spec'd parameters */
+       char                    quiet = 0;
+       char                    verbose = 0;
+       unsigned                numThreads = NUM_THREADS;
+       unsigned                numLoops = NUM_LOOPS;
+       char                    *testOpts = NULL;
+       bool                    abortOnError = false;
+       bool                    silent = false;
+       
+       for(arg=1; arg<argc; arg++) {
+               argp = argv[arg];
+               switch(argp[0]) {
+                       case 'l':
+                               numLoops = atoi(&argp[2]);
+                               break;
+                   case 't':
+                               numThreads = atoi(&argp[2]);
+                               break;
+                               break;
+                       case 'q':
+                               quiet = 1;
+                               break;
+                       case 'v':
+                               verbose = 1;
+                               break;
+                       case 'o':
+                               if((argp[1] != '=') || (argp[2] == '\0')) {
+                                       usage(argv);
+                               }
+                               testOpts = argp + 2;
+                               break;
+                       case 'e':
+                               setTestEnables(argp + 1, argv);
+                               break;
+                       case 'a':
+                               abortOnError = true;
+                               break;
+                       case 'r':
+                               startCFRunLoop();
+                               break;
+                       case 's':
+                               silent = true;
+                               quiet = 1;
+                               break;
+                       default:
+                               usage(argv);
+               }
+       }
+
+       /* attach to all three modules */
+       cspHand = cspStartup();
+       if(cspHand == 0) {
+               exit(1);
+       }
+       clHand = clStartup();
+       if(clHand == 0) {
+               goto abort;
+       }
+       tpHand = tpStartup(); 
+       if(tpHand == 0) {
+               goto abort;
+       }
+       signal(SIGPIPE, sigpipe);
+       
+       /* malloc and init TestParams for all requested threads */
+       testParams = (TestParams *)malloc(numThreads * sizeof(TestParams));
+       for(dex=0; dex<numThreads; dex++) {
+               thisTest                                = &testParams[dex];
+               thisTest->numLoops              = numLoops;
+               thisTest->verbose               = verbose;
+               thisTest->quiet                 = quiet;
+               thisTest->threadNum     = dex;
+               thisTest->cspHand               = cspHand;
+               thisTest->clHand                = clHand;
+               thisTest->tpHand                = tpHand;
+               thisTest->testOpts              = testOpts;
+               
+               if(dex < 10) {
+                       /* 0..9 */
+                       thisTest->progressChar  = '0' + dex;
+               }
+               else if(dex < 36) {
+                       /* a..z */
+                       thisTest->progressChar  = 'a' + dex - 10;
+               }
+               else {
+                       /* A..Z and if X can run more threads than that, I'll be surprised */
+                       thisTest->progressChar  = 'Z' + dex - 36;
+               }
+       }
+       
+       /* Adjust testArray for tests which are actually enabled */
+       numValidTests = 0;
+       dex=0;
+       for(i=0; i<NUM_THREAD_TESTS; i++) {
+               if(testArray[dex].enable) {
+                       numValidTests++;
+                       dex++;
+               }
+               else {
+                       /* delete this one, move remaining tests up */
+                       for(j=dex; j<NUM_THREAD_TESTS-1; j++) {
+                               testArray[j] = testArray[j+1];
+                       }
+                       /* and re-examine testArray[dex], which we just rewrote */
+               }
+       }
+       
+       if(!silent) {
+               printf("Starting threadTest; args: ");
+               for(i=1; i<(unsigned)argc; i++) {
+                       printf("%s ", argv[i]);
+               }
+               printf("\n");
+       }
+       
+       /* assign a test module to each thread and run its init routine */
+       for(dex=0; dex<numThreads; dex++) {
+               /* roll the dice */
+               thisTest = &testParams[dex];
+               thisTest->testNum = genRand(0, numValidTests - 1);
+               
+               thisTestDef = &testArray[thisTest->testNum];
+               if(!quiet) {
+                       printf("...thread %d: test %s\n", dex, thisTestDef->testName);
+               }
+               result = thisTestDef->testInit(thisTest);
+               if(result) {
+                       printf("***Error on %s init; aborting\n", thisTestDef->testName);
+                       errCount++;
+                       goto abort;
+               }
+       }
+       
+
+       /* start up each thread */
+       threadList = (pthread_t *)malloc(numThreads * sizeof(pthread_t));
+       for(dex=0; dex<numThreads; dex++) {
+               int result = pthread_create(&threadList[dex], NULL, 
+                       testThread, &testParams[dex]);
+               if(result) {
+                       printf("***pthread_create returned %d, aborting\n", result);
+                       errCount++;
+                       goto abort;
+               }
+       }
+       
+       /* wait for each thread to complete */
+       for(dex=0; dex<numThreads; dex++) {
+               void *status;
+               result = pthread_join(threadList[dex], &status);
+               if(result) {
+                       printf("***pthread_join returned %d, aborting\n", result);
+                       goto abort;
+               }
+               if(!quiet) {
+                       printf("\n...joined thread %d, status %d\n", 
+                               dex, status ? 1 : 0);
+               }
+               if(status != NULL) {
+                       errCount++;
+                       if(abortOnError) {
+                               break;
+                       }
+               }
+       }
+       if(errCount || !quiet) {
+               printf("threadTest complete; errCount %d\n", errCount);
+       }
+abort:
+       if(cspHand != 0) {
+               CSSM_ModuleDetach(cspHand);
+       }
+       if(clHand != 0) {
+               CSSM_ModuleDetach(clHand);
+       }
+       if(tpHand != 0) {
+               CSSM_ModuleDetach(tpHand);
+       }
+       return errCount;
+}
+
+