From c79223040d23678e79914e1e6afddea0487b3a6e Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Sun, 18 Jun 2000 15:59:04 +0000 Subject: [PATCH] Add support for dynamically created and destroyed mutexes. This will be needed in some ENGINE code, and might serve elsewhere as well. Note that it's implemented in such a way that the locking itself is done through the same CRYPTO_lock function as the static locks. WARNING: This is currently experimental and untested code (it will get tested soon, though :-)). --- crypto/cpt_err.c | 2 + crypto/cryptlib.c | 139 +++++++++++++++++++++++++++++++++++++-- crypto/crypto.h | 19 ++++++ crypto/stack/safestack.h | 20 ++++++ doc/crypto/threads.pod | 66 ++++++++++++++++++- util/libeay.num | 7 ++ 6 files changed, 247 insertions(+), 6 deletions(-) diff --git a/crypto/cpt_err.c b/crypto/cpt_err.c index dadd8d8d92..7018b74ca0 100644 --- a/crypto/cpt_err.c +++ b/crypto/cpt_err.c @@ -67,6 +67,7 @@ static ERR_STRING_DATA CRYPTO_str_functs[]= { {ERR_PACK(0,CRYPTO_F_CRYPTO_GET_EX_NEW_INDEX,0), "CRYPTO_get_ex_new_index"}, +{ERR_PACK(0,CRYPTO_F_CRYPTO_GET_NEW_DYNLOCKID,0), "CRYPTO_get_new_dynlockid"}, {ERR_PACK(0,CRYPTO_F_CRYPTO_GET_NEW_LOCKID,0), "CRYPTO_get_new_lockid"}, {ERR_PACK(0,CRYPTO_F_CRYPTO_SET_EX_DATA,0), "CRYPTO_set_ex_data"}, {0,NULL} @@ -74,6 +75,7 @@ static ERR_STRING_DATA CRYPTO_str_functs[]= static ERR_STRING_DATA CRYPTO_str_reasons[]= { +{CRYPTO_R_NO_DYNLOCK_CREATE_CALLBACK ,"no dynlock create callback"}, {0,NULL} }; diff --git a/crypto/cryptlib.c b/crypto/cryptlib.c index 90156d0072..bc692720c5 100644 --- a/crypto/cryptlib.c +++ b/crypto/cryptlib.c @@ -60,11 +60,15 @@ #include #include "cryptlib.h" #include +#include #if defined(WIN32) || defined(WIN16) static double SSLeay_MSVC5_hack=0.0; /* and for VC1.5 */ #endif +DECLARE_STACK_OF(CRYPTO_dynlock); +IMPLEMENT_STACK_OF(CRYPTO_dynlock); + /* real #defines in crypto.h, keep these upto date */ static const char* lock_names[CRYPTO_NUM_LOCKS] = { @@ -100,13 +104,30 @@ static const char* lock_names[CRYPTO_NUM_LOCKS] = #endif }; +/* This is for applications to allocate new type names in the non-dynamic + array of lock names. These are numbered with positive numbers. */ static STACK *app_locks=NULL; +/* For applications that want a more dynamic way of handling threads, the + following stack is used. These are externally numbered with negative + numbers. */ +static STACK_OF(CRYPTO_dynlock) *dyn_locks=NULL; + + static void (MS_FAR *locking_callback)(int mode,int type, const char *file,int line)=NULL; static int (MS_FAR *add_lock_callback)(int *pointer,int amount, int type,const char *file,int line)=NULL; static unsigned long (MS_FAR *id_callback)(void)=NULL; +static CRYPTO_dynlock *(MS_FAR *dynlock_create_callback)(const char *file, + int line)=NULL; +static void (MS_FAR *dynlock_locking_callback)(int mode, CRYPTO_dynlock *l, + const char *file,int line)=NULL; +static void (MS_FAR *dynlock_destroy_callback)(CRYPTO_dynlock *l, + const char *file,int line)=NULL; +static int (MS_FAR *add_dynlock_callback)(int *pointer,int amount, + CRYPTO_dynlock *l,const char *file,int line)=NULL; + int CRYPTO_get_new_lockid(char *name) { char *str; @@ -126,7 +147,10 @@ int CRYPTO_get_new_lockid(char *name) return(0); } if ((str=BUF_strdup(name)) == NULL) + { + CRYPTOerr(CRYPTO_F_CRYPTO_GET_NEW_LOCKID,ERR_R_MALLOC_FAILURE); return(0); + } i=sk_push(app_locks,str); if (!i) OPENSSL_free(str); @@ -140,30 +164,112 @@ int CRYPTO_num_locks(void) return CRYPTO_NUM_LOCKS; } +int CRYPTO_get_new_dynlockid(void) + { + int i = 0; + CRYPTO_dynlock *pointer = NULL; + + if (dynlock_create_callback == NULL) + { + CRYPTOerr(CRYPTO_F_CRYPTO_GET_NEW_DYNLOCKID,CRYPTO_R_NO_DYNLOCK_CREATE_CALLBACK); + return(0); + } + if ((dyn_locks == NULL) + && ((dyn_locks=sk_new_null()) == NULL)) + { + CRYPTOerr(CRYPTO_F_CRYPTO_GET_NEW_DYNLOCKID,ERR_R_MALLOC_FAILURE); + return(0); + } + + pointer = dynlock_create_callback(__FILE__,__LINE__); + if (pointer == NULL) + { + CRYPTOerr(CRYPTO_F_CRYPTO_GET_NEW_DYNLOCKID,ERR_R_MALLOC_FAILURE); + return(0); + } + i=sk_CRYPTO_dynlock_push(dyn_locks,pointer); + if (!i) + dynlock_destroy_callback(pointer,__FILE__,__LINE__); + else + i += 1; /* to avoid 0 */ + return -i; + } + +void CRYPTO_destroy_dynlockid(int i) + { + if (i) + i = -i-1; + if (dyn_locks == NULL || i >= sk_CRYPTO_dynlock_num(dyn_locks)) + return; + if (dynlock_destroy_callback == NULL) + return; + dynlock_destroy_callback(sk_CRYPTO_dynlock_value(dyn_locks, i), + __FILE__,__LINE__); + sk_CRYPTO_dynlock_set(dyn_locks, i, NULL); + } + +CRYPTO_dynlock *CRYPTO_get_dynlock_value(int i) + { + if (i) + i = -i-1; + if (dyn_locks == NULL || i >= sk_CRYPTO_dynlock_num(dyn_locks)) + return NULL; + return sk_CRYPTO_dynlock_value(dyn_locks, i); + } + void (*CRYPTO_get_locking_callback(void))(int mode,int type,const char *file, int line) { return(locking_callback); } +void (*CRYPTO_get_dynlock_lock_callback(void))(int mode,CRYPTO_dynlock *l, + const char *file,int line) + { + return(dynlock_locking_callback); + } + int (*CRYPTO_get_add_lock_callback(void))(int *num,int mount,int type, const char *file,int line) { return(add_lock_callback); } +int (*CRYPTO_get_add_dynlock_callback(void))(int *num,int mount, + CRYPTO_dynlock *l, + const char *file,int line) + { + return(add_dynlock_callback); + } + void CRYPTO_set_locking_callback(void (*func)(int mode,int type, const char *file,int line)) { locking_callback=func; } +void CRYPTO_set_dynlock_locking_callback(void (*func)(int mode, + CRYPTO_dynlock *l, + const char *file, + int line)) + { + dynlock_locking_callback=func; + } + void CRYPTO_set_add_lock_callback(int (*func)(int *num,int mount,int type, const char *file,int line)) { add_lock_callback=func; } +void CRYPTO_set_add_dynlock_lock_callback(int (*func)(int *num,int mount, + CRYPTO_dynlock *l, + const char *file, + int line)) + { + add_dynlock_callback=func; + } + unsigned long (*CRYPTO_get_id_callback(void))(void) { return(id_callback); @@ -220,14 +326,23 @@ void CRYPTO_lock(int mode, int type, const char *file, int line) CRYPTO_get_lock_name(type), file, line); } #endif - if (locking_callback != NULL) - locking_callback(mode,type,file,line); + if (type < 0) + { + int i = -type-1; + if (i < sk_CRYPTO_dynlock_num(dyn_locks)) + dynlock_locking_callback(mode, + sk_CRYPTO_dynlock_value(dyn_locks,i), + file,line); + } + else + if (locking_callback != NULL) + locking_callback(mode,type,file,line); } int CRYPTO_add_lock(int *pointer, int amount, int type, const char *file, int line) { - int ret; + int ret = 0; if (add_lock_callback != NULL) { @@ -235,7 +350,21 @@ int CRYPTO_add_lock(int *pointer, int amount, int type, const char *file, int before= *pointer; #endif - ret=add_lock_callback(pointer,amount,type,file,line); + if (type < 0) + { + int i = -type-1; + if (i >= sk_CRYPTO_dynlock_num(dyn_locks)) + /* FIXME: This is superbly dangerous if there + are threads competing on this value, but + hey, if the user used an invalid lock... */ + ret=(*pointer + amount); + else + ret=add_dynlock_callback(pointer,amount, + sk_CRYPTO_dynlock_value(dyn_locks,i), + file,line); + } + else + ret=add_lock_callback(pointer,amount,type,file,line); #ifdef LOCK_DEBUG fprintf(stderr,"ladd:%08lx:%2d+%2d->%2d %-18s %s:%d\n", CRYPTO_thread_id(), @@ -266,7 +395,7 @@ int CRYPTO_add_lock(int *pointer, int amount, int type, const char *file, const char *CRYPTO_get_lock_name(int type) { if (type < 0) - return("ERROR"); + return("dynamic"); else if (type < CRYPTO_NUM_LOCKS) return(lock_names[type]); else if (type-CRYPTO_NUM_LOCKS >= sk_num(app_locks)) diff --git a/crypto/crypto.h b/crypto/crypto.h index 9a3a6f8b00..9e5f2e2922 100644 --- a/crypto/crypto.h +++ b/crypto/crypto.h @@ -150,6 +150,12 @@ extern "C" { #define CRYPTO_add(a,b,c) ((*(a))+=(b)) #endif +/* Some applications as well as some parts of OpenSSL need to allocate + and deallocate locks in a dynamic fashion. The following typedef + makes this possible in a type-safe manner. */ +typedef struct CRYPTO_dynlock_value CRYPTO_dynlock; + + /* The following can be used to detect memory leaks in the SSLeay library. * It used, it turns on malloc checking */ @@ -299,6 +305,16 @@ unsigned long CRYPTO_thread_id(void); const char *CRYPTO_get_lock_name(int type); int CRYPTO_add_lock(int *pointer,int amount,int type, const char *file, int line); +void CRYPTO_set_dynlock_create_callback(CRYPTO_dynlock *(*dyn_create_function) + (char *file, int line)); +void CRYPTO_set_dynlock_lock_callback(void (*dyn_lock_function) + (int mode, CRYPTO_dynlock *l, const char *file, int line)); +void CRYPTO_set_dynlock_destroy_callback(void (*dyn_destroy_function) + (CRYPTO_dynlock *l, const char *file, int line)); +void CRYPTO_set_dynlock_size(int dynlock_size); +int CRYPTO_get_new_dynlockid(void); +void CRYPTO_destroy_dynlockid(int i); +CRYPTO_dynlock *CRYPTO_get_dynlock_value(int i); /* CRYPTO_set_mem_functions includes CRYPTO_set_locked_mem_functions -- * call the latter last if you need different functions */ @@ -371,12 +387,15 @@ void ERR_load_CRYPTO_strings(void); /* Function codes. */ #define CRYPTO_F_CRYPTO_GET_EX_NEW_INDEX 100 +#define CRYPTO_F_CRYPTO_GET_NEW_DYNLOCKID 103 #define CRYPTO_F_CRYPTO_GET_NEW_LOCKID 101 #define CRYPTO_F_CRYPTO_SET_EX_DATA 102 /* Reason codes. */ +#define CRYPTO_R_NO_DYNLOCK_CREATE_CALLBACK 100 #ifdef __cplusplus } #endif #endif + diff --git a/crypto/stack/safestack.h b/crypto/stack/safestack.h index 8cc022b456..1a550244ac 100644 --- a/crypto/stack/safestack.h +++ b/crypto/stack/safestack.h @@ -164,6 +164,26 @@ STACK_OF(type) \ #endif /* This block of defines is updated by util/mkstack.pl, please do not touch! */ +#define sk_CRYPTO_dynlock_new(st) SKM_sk_new(CRYPTO_dynlock, (st)) +#define sk_CRYPTO_dynlock_new_null() SKM_sk_new_null(CRYPTO_dynlock) +#define sk_CRYPTO_dynlock_free(st) SKM_sk_free(CRYPTO_dynlock, (st)) +#define sk_CRYPTO_dynlock_num(st) SKM_sk_num(CRYPTO_dynlock, (st)) +#define sk_CRYPTO_dynlock_value(st, i) SKM_sk_value(CRYPTO_dynlock, (st), (i)) +#define sk_CRYPTO_dynlock_set(st, i, val) SKM_sk_set(CRYPTO_dynlock, (st), (i), (val)) +#define sk_CRYPTO_dynlock_zero(st) SKM_sk_zero(CRYPTO_dynlock, (st)) +#define sk_CRYPTO_dynlock_push(st, val) SKM_sk_push(CRYPTO_dynlock, (st), (val)) +#define sk_CRYPTO_dynlock_unshift(st, val) SKM_sk_unshift(CRYPTO_dynlock, (st), (val)) +#define sk_CRYPTO_dynlock_find(st, val) SKM_sk_find(CRYPTO_dynlock, (st), (val)) +#define sk_CRYPTO_dynlock_delete(st, i) SKM_sk_delete(CRYPTO_dynlock, (st), (i)) +#define sk_CRYPTO_dynlock_delete_ptr(st, ptr) SKM_sk_delete_ptr(CRYPTO_dynlock, (st), (ptr)) +#define sk_CRYPTO_dynlock_insert(st, val, i) SKM_sk_insert(CRYPTO_dynlock, (st), (val), (i)) +#define sk_CRYPTO_dynlock_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(CRYPTO_dynlock, (st), (cmp)) +#define sk_CRYPTO_dynlock_dup(st) SKM_sk_dup(CRYPTO_dynlock, st) +#define sk_CRYPTO_dynlock_pop_free(st, free_func) SKM_sk_pop_free(CRYPTO_dynlock, (st), (free_func)) +#define sk_CRYPTO_dynlock_shift(st) SKM_sk_shift(CRYPTO_dynlock, (st)) +#define sk_CRYPTO_dynlock_pop(st) SKM_sk_pop(CRYPTO_dynlock, (st)) +#define sk_CRYPTO_dynlock_sort(st) SKM_sk_sort(CRYPTO_dynlock, (st)) + #define sk_CRYPTO_EX_DATA_FUNCS_new(st) SKM_sk_new(CRYPTO_EX_DATA_FUNCS, (st)) #define sk_CRYPTO_EX_DATA_FUNCS_new_null() SKM_sk_new_null(CRYPTO_EX_DATA_FUNCS) #define sk_CRYPTO_EX_DATA_FUNCS_free(st) SKM_sk_free(CRYPTO_EX_DATA_FUNCS, (st)) diff --git a/doc/crypto/threads.pod b/doc/crypto/threads.pod index 5da056f3f8..a31b170806 100644 --- a/doc/crypto/threads.pod +++ b/doc/crypto/threads.pod @@ -15,10 +15,27 @@ CRYPTO_set_locking_callback, CRYPTO_set_id_callback - OpenSSL thread support int CRYPTO_num_locks(void); + + /* struct CRYPTO_dynlock_value needs to be defined by the user */ + typedef struct CRYPTO_dynlock_value CRYPTO_dynlock; + + void CRYPTO_set_dynlock_create_callback(CRYPTO_dynlock *(*dyn_create_function) + (char *file, int line)); + void CRYPTO_set_dynlock_lock_callback(void (*dyn_lock_function) + (int mode, CRYPTO_dynlock *l, const char *file, int line)); + void CRYPTO_set_dynlock_destroy_callback(void (*dyn_destroy_function) + (CRYPTO_dynlock *l, const char *file, int line)); + + int CRYPTO_get_new_dynlockid(void); + + void CRYPTO_destroy_dynlockid(int i); + + void CRYPTO_lock(int mode, int n, const char *file, int line); + =head1 DESCRIPTION OpenSSL can safely be used in multi-threaded applications provided -that two callback functions are set. +that at least two callback functions are set. locking_function(int mode, int n, const char *file, int line) is needed to perform locking on shared data stuctures. Multi-threaded @@ -35,9 +52,55 @@ id_function(void) is a function that returns a thread ID. It is not needed on Windows nor on platforms where getpid() returns a different ID for each thread (most notably Linux). +Additionally, OpenSSL supports dynamic locks, and sometimes, some parts +of OpenSSL need it for better performance. To enable this, the following +is required: + +=item * +Three additional callback function, dyn_create_function, dyn_lock_function +and dyn_destroy_function. + +=item * +A structure defined with the data that each lock needs to handle. + +struct CRYPTO_dynlock_value has to be defined to contain whatever structure +is needed to handle locks. + +dyn_create_function(const char *file, int line) is needed to create a +lock. Multi-threaded applications might crash at random if it is not set. + +dyn_lock_function(int mode, CRYPTO_dynlock *l, const char *file, int line) +is needed to perform locking off dynamic lock nunmbered n. Multi-threaded +applications might crash at random if it is not set. + +dyn_destroy_function(CRYPTO_dynlock *l, const char *file, int line) is +needed to destroy the lock l. Multi-threaded applications might crash at +random if it is not set. + +CRYPTO_get_new_dynlockid() is used to create locks. It will call +dyn_create_function for the actual creation. + +CRYPTO_destroy_dynlockid() is used to destroy locks. It will call +dyn_destroy_function for the actual destruction. + +CRYPTO_lock() is used to lock and unlock the locks. mode is a bitfield +describing what should be done with the lock. n is the number of the +lock as returned from CRYPTO_get_new_dynlockid(). mode can be combined +from the following values. These values are pairwise exclusive, with +undefined behavior if misused (for example, CRYPTO_READ and CRYPTO_WRITE +should not be used together): + + CRYPTO_LOCK 0x01 + CRYPTO_UNLOCK 0x02 + CRYPTO_READ 0x04 + CRYPTO_WRITE 0x08 + =head1 RETURN VALUES CRYPTO_num_locks() returns the required number of locks. + +CRYPTO_get_new_dynlockid() returns the index to the newly created lock. + The other functions return no values. =head1 NOTE @@ -62,6 +125,7 @@ Solaris, Irix and Win32. CRYPTO_set_locking_callback() and CRYPTO_set_id_callback() are available in all versions of SSLeay and OpenSSL. CRYPTO_num_locks() was added in OpenSSL 0.9.4. +All functions dealing with dynamic locks were added in OpenSSL 0.9.5b-dev. =head1 SEE ALSO diff --git a/util/libeay.num b/util/libeay.num index fdb90afd5c..311afa19fe 100755 --- a/util/libeay.num +++ b/util/libeay.num @@ -1810,3 +1810,10 @@ i2d_RSA_NET 2406 d2i_RSA_NET_2 2407 d2i_RSA_NET 2408 DSO_bind_func 2409 +CRYPTO_get_new_dynlockid 2410 +sk_new_null 2411 +CRYPTO_set_dynlock_destroy_callback 2412 +CRYPTO_destroy_dynlockid 2413 +CRYPTO_set_dynlock_size 2414 +CRYPTO_set_dynlock_create_callback 2415 +CRYPTO_set_dynlock_lock_callback 2416 -- 2.40.0