fix md_rand.c locking bugs

This commit is contained in:
Bodo Möller 2001-04-18 15:07:35 +00:00
parent 48ff225300
commit 6e6d04e29a
3 changed files with 67 additions and 20 deletions

13
CHANGES
View File

@ -11,6 +11,19 @@
*) applies to 0.9.6a (/0.9.6b) and 0.9.7 *) applies to 0.9.6a (/0.9.6b) and 0.9.7
+) applies to 0.9.7 only +) applies to 0.9.7 only
*) Move 'if (!initialized) RAND_poll()' into regions protected by
CRYPTO_LOCK_RAND. This is not strictly necessary, but avoids
having multiple threads calling RAND_poll() concurrently.
[Bodo Moeller]
*) In crypto/rand/md_rand.c, replace 'add_do_not_lock' flag by a
combination of a flag and a thread ID variable.
Otherwise while one thread is in ssleay_rand_bytes (which sets the
flag), *other* threads can enter ssleay_add_bytes without obeying
the CRYPTO_LOCK_RAND lock (and may even illegaly release the lock
that they do not hold after the first thread unsets add_do_not_lock).
[Bodo Moeller]
+) Implement binary inversion algorithm for BN_mod_inverse in addition +) Implement binary inversion algorithm for BN_mod_inverse in addition
to the algorithm using long divison. The binary algorithm can be to the algorithm using long divison. The binary algorithm can be
used only if the modulus is odd. On 32-bit systems, it is faster used only if the modulus is odd. On 32-bit systems, it is faster

View File

@ -141,10 +141,11 @@ static long md_count[2]={0,0};
static double entropy=0; static double entropy=0;
static int initialized=0; static int initialized=0;
/* This should be set to 1 only when ssleay_rand_add() is called inside static unsigned int crypto_lock_rand = 0; /* may be set only when a thread
an already locked state, so it doesn't try to lock and thereby cause * holds CRYPTO_LOCK_RAND
a hang. And it should always be reset back to 0 before unlocking. */ * (to prevent double locking) */
static int add_do_not_lock=0; static unsigned long locking_thread = 0; /* valid iff crypto_lock_rand is set */
#ifdef PREDICT #ifdef PREDICT
int rand_predictable=0; int rand_predictable=0;
@ -191,6 +192,7 @@ static void ssleay_rand_add(const void *buf, int num, double add)
long md_c[2]; long md_c[2];
unsigned char local_md[MD_DIGEST_LENGTH]; unsigned char local_md[MD_DIGEST_LENGTH];
MD_CTX m; MD_CTX m;
int do_not_lock;
/* /*
* (Based on the rand(3) manpage) * (Based on the rand(3) manpage)
@ -207,7 +209,10 @@ static void ssleay_rand_add(const void *buf, int num, double add)
* hash function. * hash function.
*/ */
if (!add_do_not_lock) CRYPTO_w_lock(CRYPTO_LOCK_RAND); /* check if we already have the lock */
do_not_lock = crypto_lock_rand && (locking_thread == CRYPTO_thread_id());
if (!do_not_lock) CRYPTO_w_lock(CRYPTO_LOCK_RAND);
st_idx=state_index; st_idx=state_index;
/* use our own copies of the counters so that even /* use our own copies of the counters so that even
@ -239,7 +244,7 @@ static void ssleay_rand_add(const void *buf, int num, double add)
md_count[1] += (num / MD_DIGEST_LENGTH) + (num % MD_DIGEST_LENGTH > 0); md_count[1] += (num / MD_DIGEST_LENGTH) + (num % MD_DIGEST_LENGTH > 0);
if (!add_do_not_lock) CRYPTO_w_unlock(CRYPTO_LOCK_RAND); if (!do_not_lock) CRYPTO_w_unlock(CRYPTO_LOCK_RAND);
for (i=0; i<num; i+=MD_DIGEST_LENGTH) for (i=0; i<num; i+=MD_DIGEST_LENGTH)
{ {
@ -281,7 +286,7 @@ static void ssleay_rand_add(const void *buf, int num, double add)
} }
memset((char *)&m,0,sizeof(m)); memset((char *)&m,0,sizeof(m));
if (!add_do_not_lock) CRYPTO_w_lock(CRYPTO_LOCK_RAND); if (!do_not_lock) CRYPTO_w_lock(CRYPTO_LOCK_RAND);
/* Don't just copy back local_md into md -- this could mean that /* Don't just copy back local_md into md -- this could mean that
* other thread's seeding remains without effect (except for * other thread's seeding remains without effect (except for
* the incremented counter). By XORing it we keep at least as * the incremented counter). By XORing it we keep at least as
@ -292,7 +297,7 @@ static void ssleay_rand_add(const void *buf, int num, double add)
} }
if (entropy < ENTROPY_NEEDED) /* stop counting when we have enough */ if (entropy < ENTROPY_NEEDED) /* stop counting when we have enough */
entropy += add; entropy += add;
if (!add_do_not_lock) CRYPTO_w_unlock(CRYPTO_LOCK_RAND); if (!do_not_lock) CRYPTO_w_unlock(CRYPTO_LOCK_RAND);
#if !defined(OPENSSL_THREADS) && !defined(OPENSSL_SYS_WIN32) #if !defined(OPENSSL_THREADS) && !defined(OPENSSL_SYS_WIN32)
assert(md_c[1] == md_count[1]); assert(md_c[1] == md_count[1]);
@ -347,14 +352,18 @@ static int ssleay_rand_bytes(unsigned char *buf, int num)
* global 'md'. * global 'md'.
*/ */
if (!initialized)
RAND_poll();
CRYPTO_w_lock(CRYPTO_LOCK_RAND); CRYPTO_w_lock(CRYPTO_LOCK_RAND);
add_do_not_lock = 1; /* Since we call ssleay_rand_add while in
this locked state. */
initialized = 1; /* prevent ssleay_rand_bytes() from trying to obtain the lock again */
crypto_lock_rand = 1;
locking_thread = CRYPTO_thread_id();
if (!initialized)
{
RAND_poll();
initialized = 1;
}
if (!stirred_pool) if (!stirred_pool)
do_stir_pool = 1; do_stir_pool = 1;
@ -418,8 +427,9 @@ static int ssleay_rand_bytes(unsigned char *buf, int num)
md_count[0] += 1; md_count[0] += 1;
add_do_not_lock = 0; /* If this would ever be forgotten, we can /* before unlocking, we must clear 'crypto_lock_rand' */
expect any evil god to eat our souls. */ crypto_lock_rand = 0;
locking_thread = 0;
CRYPTO_w_unlock(CRYPTO_LOCK_RAND); CRYPTO_w_unlock(CRYPTO_LOCK_RAND);
while (num > 0) while (num > 0)
@ -498,14 +508,37 @@ static int ssleay_rand_pseudo_bytes(unsigned char *buf, int num)
static int ssleay_rand_status(void) static int ssleay_rand_status(void)
{ {
int ret; int ret;
int do_not_lock;
/* check if we already have the lock
* (could happen if a RAND_poll() implementation calls RAND_status()) */
do_not_lock = crypto_lock_rand && (locking_thread == CRYPTO_thread_id());
if (!do_not_lock)
{
CRYPTO_w_lock(CRYPTO_LOCK_RAND);
/* prevent ssleay_rand_bytes() from trying to obtain the lock again */
crypto_lock_rand = 1;
locking_thread = CRYPTO_thread_id();
}
if (!initialized) if (!initialized)
{
RAND_poll(); RAND_poll();
initialized = 1;
}
CRYPTO_w_lock(CRYPTO_LOCK_RAND);
initialized = 1;
ret = entropy >= ENTROPY_NEEDED; ret = entropy >= ENTROPY_NEEDED;
CRYPTO_w_unlock(CRYPTO_LOCK_RAND);
if (!do_not_lock)
{
/* before unlocking, we must clear 'crypto_lock_rand' */
crypto_lock_rand = 0;
locking_thread = 0;
CRYPTO_w_unlock(CRYPTO_LOCK_RAND);
}
return ret; return ret;
} }

View File

@ -228,8 +228,9 @@ int RAND_poll(void)
#if defined(DEVRANDOM) || defined(DEVRANDOM_EGD) #if defined(DEVRANDOM) || defined(DEVRANDOM_EGD)
return 1; return 1;
#endif #else
return 0; return 0;
#endif
} }
#endif #endif