diff options
author | Sverker Eriksson <[email protected]> | 2012-10-05 11:52:52 +0200 |
---|---|---|
committer | Sverker Eriksson <[email protected]> | 2012-10-09 22:05:11 +0200 |
commit | e5c1d346e29e5b1227ed30ee4d725a09eca0e532 (patch) | |
tree | b5c8114a810459e855d4a81ec918c40e240c82d9 /lib/crypto/c_src/crypto_callback.c | |
parent | 54e7cc9086908a0e5642caa6ac3680557acc5a67 (diff) | |
download | otp-e5c1d346e29e5b1227ed30ee4d725a09eca0e532.tar.gz otp-e5c1d346e29e5b1227ed30ee4d725a09eca0e532.tar.bz2 otp-e5c1d346e29e5b1227ed30ee4d725a09eca0e532.zip |
crypto: Make unloading of crypto safer
Facts:
crypto nif-lib registers callback functions that openssl uses
for memory management and thread synchronization. The callback
functions can only be set once, openssl does not allow changing the
callback functions.
Problem:
If openssl is dynamicly linked to crypto, you might get s scenario
where the crypto lib is unloaded while leaving openssl loaded
with its old pointers to the unloaded crypto code intact.
If crypto is then reloaded (by init:restart() for example), the crypto
nif-lib might get relocated at a different address. crypto calls
openssl which in turn calls the old invalid callback functions...kaboom.
Solution:
Break apart the callback functions into a separate dynamic lib that
crypto loads with dlopen. When crypto is unloaded the callback lib is
left in place to be reused if/when crypto is loaded again.
Diffstat (limited to 'lib/crypto/c_src/crypto_callback.c')
-rw-r--r-- | lib/crypto/c_src/crypto_callback.c | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/lib/crypto/c_src/crypto_callback.c b/lib/crypto/c_src/crypto_callback.c new file mode 100644 index 0000000000..32e690a8d1 --- /dev/null +++ b/lib/crypto/c_src/crypto_callback.c @@ -0,0 +1,163 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +#include <string.h> +#include <openssl/opensslconf.h> + +#include "erl_nif.h" +#include "crypto_callback.h" + +#ifdef DEBUG + # define ASSERT(e) \ + ((void) ((e) ? 1 : (fprintf(stderr,"Assert '%s' failed at %s:%d\n",\ + #e, __FILE__, __LINE__), abort(), 0))) +#else + # define ASSERT(e) ((void) 1) +#endif + +#ifdef __GNUC__ + # define INLINE __inline__ +#elif defined(__WIN32__) + # define INLINE __forceinline +#else + # define INLINE +#endif + +#ifdef __WIN32__ +# define DLLEXPORT __declspec(dllexport) +#else +# define DLLEXPORT +#endif + +/* to be dlsym'ed */ +DLLEXPORT struct crypto_callbacks* get_crypto_callbacks(int nlocks); + + +static ErlNifRWLock** lock_vec = NULL; /* Static locks used by openssl */ + +static void* crypto_alloc(size_t size) +{ + return enif_alloc(size); +} +static void* crypto_realloc(void* ptr, size_t size) +{ + return enif_realloc(ptr, size); +} +static void crypto_free(void* ptr) +{ + enif_free(ptr); +} + + +#ifdef OPENSSL_THREADS /* vvvvvvvvvvvvvvv OPENSSL_THREADS vvvvvvvvvvvvvvvv */ + +#include <openssl/crypto.h> + +static INLINE void locking(int mode, ErlNifRWLock* lock) +{ + switch (mode) { + case CRYPTO_LOCK|CRYPTO_READ: + enif_rwlock_rlock(lock); + break; + case CRYPTO_LOCK|CRYPTO_WRITE: + enif_rwlock_rwlock(lock); + break; + case CRYPTO_UNLOCK|CRYPTO_READ: + enif_rwlock_runlock(lock); + break; + case CRYPTO_UNLOCK|CRYPTO_WRITE: + enif_rwlock_rwunlock(lock); + break; + default: + ASSERT(!"Invalid lock mode"); + } +} + +static void locking_function(int mode, int n, const char *file, int line) +{ + ASSERT(n>=0 && n<CRYPTO_num_locks()); + + locking(mode, lock_vec[n]); +} + +static unsigned long id_function(void) +{ + return (unsigned long) enif_thread_self(); +} + +/* Dynamic locking, not used by current openssl version (0.9.8) + */ +static struct CRYPTO_dynlock_value* dyn_create_function(const char *file, int line) +{ + return (struct CRYPTO_dynlock_value*) enif_rwlock_create("crypto_dyn"); +} +static void dyn_lock_function(int mode, struct CRYPTO_dynlock_value* ptr,const char *file, int line) +{ + locking(mode, (ErlNifRWLock*)ptr); +} +static void dyn_destroy_function(struct CRYPTO_dynlock_value *ptr, const char *file, int line) +{ + enif_rwlock_destroy((ErlNifRWLock*)ptr); +} + +#endif /* ^^^^^^^^^^^^^^^^^^^^^^ OPENSSL_THREADS ^^^^^^^^^^^^^^^^^^^^^^ */ + +DLLEXPORT struct crypto_callbacks* get_crypto_callbacks(int nlocks) +{ + static int is_initialized = 0; + static struct crypto_callbacks the_struct = { + sizeof(struct crypto_callbacks), + + &crypto_alloc, + &crypto_realloc, + &crypto_free, + +#ifdef OPENSSL_THREADS + &locking_function, + &id_function, + &dyn_create_function, + &dyn_lock_function, + &dyn_destroy_function +#endif /* OPENSSL_THREADS */ + }; + + if (!is_initialized) { +#ifdef OPENSSL_THREADS + if (nlocks > 0) { + int i; + lock_vec = enif_alloc(nlocks*sizeof(*lock_vec)); + if (lock_vec==NULL) return NULL; + memset(lock_vec, 0, nlocks*sizeof(*lock_vec)); + + for (i=nlocks-1; i>=0; --i) { + lock_vec[i] = enif_rwlock_create("crypto_stat"); + if (lock_vec[i]==NULL) return NULL; + } + } +#endif + is_initialized = 1; + } + return &the_struct; +} + +/* This is not really a NIF library, but we use ERL_NIF_INIT in order to + * get access to the erl_nif API (on Windows). + */ +ERL_NIF_INIT(dummy, (ErlNifFunc*)NULL , NULL, NULL, NULL, NULL) + |