Skip to content

Commit 4945d36

Browse files
authored
Merge pull request #155 from rhenium/ky/fix-legacy-locking-callback-relock
ossl.c: make legacy locking callbacks reentrant
2 parents 295dd1d + b2b2fb8 commit 4945d36

File tree

1 file changed

+40
-26
lines changed

1 file changed

+40
-26
lines changed

ext/openssl/ossl.c

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -484,40 +484,53 @@ print_mem_leaks(VALUE self)
484484
/**
485485
* Stores locks needed for OpenSSL thread safety
486486
*/
487-
static rb_nativethread_lock_t *ossl_locks;
487+
struct CRYPTO_dynlock_value {
488+
rb_nativethread_lock_t lock;
489+
rb_nativethread_id_t owner;
490+
size_t count;
491+
};
488492

489493
static void
490-
ossl_lock_unlock(int mode, rb_nativethread_lock_t *lock)
494+
ossl_lock_init(struct CRYPTO_dynlock_value *l)
491495
{
492-
if (mode & CRYPTO_LOCK) {
493-
rb_nativethread_lock_lock(lock);
494-
} else {
495-
rb_nativethread_lock_unlock(lock);
496-
}
496+
rb_nativethread_lock_initialize(&l->lock);
497+
l->count = 0;
497498
}
498499

499500
static void
500-
ossl_lock_callback(int mode, int type, const char *file, int line)
501+
ossl_lock_unlock(int mode, struct CRYPTO_dynlock_value *l)
501502
{
502-
ossl_lock_unlock(mode, &ossl_locks[type]);
503+
if (mode & CRYPTO_LOCK) {
504+
/* TODO: rb_nativethread_id_t is not necessarily compared with ==. */
505+
rb_nativethread_id_t tid = rb_nativethread_self();
506+
if (l->count && l->owner == tid) {
507+
l->count++;
508+
return;
509+
}
510+
rb_nativethread_lock_lock(&l->lock);
511+
l->owner = tid;
512+
l->count = 1;
513+
} else {
514+
if (!--l->count)
515+
rb_nativethread_lock_unlock(&l->lock);
516+
}
503517
}
504518

505-
struct CRYPTO_dynlock_value {
506-
rb_nativethread_lock_t lock;
507-
};
508-
509519
static struct CRYPTO_dynlock_value *
510520
ossl_dyn_create_callback(const char *file, int line)
511521
{
512-
struct CRYPTO_dynlock_value *dynlock = (struct CRYPTO_dynlock_value *)OPENSSL_malloc((int)sizeof(struct CRYPTO_dynlock_value));
513-
rb_nativethread_lock_initialize(&dynlock->lock);
522+
/* Do not use xmalloc() here, since it may raise NoMemoryError */
523+
struct CRYPTO_dynlock_value *dynlock =
524+
OPENSSL_malloc(sizeof(struct CRYPTO_dynlock_value));
525+
if (dynlock)
526+
ossl_lock_init(dynlock);
514527
return dynlock;
515528
}
516529

517530
static void
518531
ossl_dyn_lock_callback(int mode, struct CRYPTO_dynlock_value *l, const char *file, int line)
519532
{
520-
ossl_lock_unlock(mode, &l->lock);
533+
ossl_lock_unlock(mode, l);
521534
}
522535

523536
static void
@@ -541,21 +554,22 @@ static unsigned long ossl_thread_id(void)
541554
}
542555
#endif
543556

557+
static struct CRYPTO_dynlock_value *ossl_locks;
558+
559+
static void
560+
ossl_lock_callback(int mode, int type, const char *file, int line)
561+
{
562+
ossl_lock_unlock(mode, &ossl_locks[type]);
563+
}
564+
544565
static void Init_ossl_locks(void)
545566
{
546567
int i;
547568
int num_locks = CRYPTO_num_locks();
548569

549-
if ((unsigned)num_locks >= INT_MAX / (int)sizeof(VALUE)) {
550-
rb_raise(rb_eRuntimeError, "CRYPTO_num_locks() is too big: %d", num_locks);
551-
}
552-
ossl_locks = (rb_nativethread_lock_t *) OPENSSL_malloc(num_locks * (int)sizeof(rb_nativethread_lock_t));
553-
if (!ossl_locks) {
554-
rb_raise(rb_eNoMemError, "CRYPTO_num_locks() is too big: %d", num_locks);
555-
}
556-
for (i = 0; i < num_locks; i++) {
557-
rb_nativethread_lock_initialize(&ossl_locks[i]);
558-
}
570+
ossl_locks = ALLOC_N(struct CRYPTO_dynlock_value, num_locks);
571+
for (i = 0; i < num_locks; i++)
572+
ossl_lock_init(&ossl_locks[i]);
559573

560574
#ifdef HAVE_CRYPTO_THREADID_PTR
561575
CRYPTO_THREADID_set_callback(ossl_threadid_func);

0 commit comments

Comments
 (0)