RCU: Ensure that qp's are actually retired in order

The current retirement code for rcu qp's has a race condition,
which can cause use-after-free errors, but only if more than
3 QPs are allocated, which is not the default configuration.

This fixes an oversight in commit 5949918f9a ("Rework and
simplify RCU code")

Reviewed-by: Neil Horman <nhorman@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/26952)
This commit is contained in:
Bernd Edlinger 2025-03-03 08:22:31 +01:00 committed by Tomas Mraz
parent bcb8eae1af
commit 6e7be995fd
3 changed files with 16 additions and 8 deletions

View file

@ -464,9 +464,6 @@ void ossl_synchronize_rcu(CRYPTO_RCU_LOCK *lock)
pthread_mutex_lock(&lock->prior_lock);
while (lock->next_to_retire != curr_id)
pthread_cond_wait(&lock->prior_signal, &lock->prior_lock);
lock->next_to_retire++;
pthread_cond_broadcast(&lock->prior_signal);
pthread_mutex_unlock(&lock->prior_lock);
/*
* wait for the reader count to reach zero
@ -479,6 +476,10 @@ void ossl_synchronize_rcu(CRYPTO_RCU_LOCK *lock)
count = ATOMIC_LOAD_N(uint64_t, &qp->users, __ATOMIC_ACQUIRE);
} while (count != (uint64_t)0);
lock->next_to_retire++;
pthread_cond_broadcast(&lock->prior_signal);
pthread_mutex_unlock(&lock->prior_lock);
retire_qp(lock, qp);
/* handle any callbacks that we have */

View file

@ -383,15 +383,15 @@ void ossl_synchronize_rcu(CRYPTO_RCU_LOCK *lock)
while (lock->next_to_retire != curr_id)
ossl_crypto_condvar_wait(lock->prior_signal, lock->prior_lock);
lock->next_to_retire++;
ossl_crypto_condvar_broadcast(lock->prior_signal);
ossl_crypto_mutex_unlock(lock->prior_lock);
/* wait for the reader count to reach zero */
do {
CRYPTO_atomic_load(&qp->users, &count, lock->rw_lock);
} while (count != (uint64_t)0);
lock->next_to_retire++;
ossl_crypto_condvar_broadcast(lock->prior_signal);
ossl_crypto_mutex_unlock(lock->prior_lock);
retire_qp(lock, qp);
/* handle any callbacks that we have */

View file

@ -434,7 +434,7 @@ static int _torture_rcu(void)
writer2_done = 0;
rcu_torture_result = 1;
rcu_lock = ossl_rcu_lock_new(1, NULL);
rcu_lock = ossl_rcu_lock_new(contention == 2 ? 4 : 1, NULL);
if (rcu_lock == NULL)
goto out;
@ -491,6 +491,12 @@ static int torture_rcu_high(void)
contention = 1;
return _torture_rcu();
}
static int torture_rcu_high2(void)
{
contention = 2;
return _torture_rcu();
}
#endif
static CRYPTO_ONCE once_run = CRYPTO_ONCE_STATIC_INIT;
@ -1329,6 +1335,7 @@ int setup_tests(void)
ADD_TEST(torture_rw_high);
ADD_TEST(torture_rcu_low);
ADD_TEST(torture_rcu_high);
ADD_TEST(torture_rcu_high2);
#endif
ADD_TEST(test_once);
ADD_TEST(test_thread_local);