Revert "Reduce thread contention for pooled CRYPTO_BUFFERs" This reverts commit fbf01ffae10929d863b6744bb505f0cae6c9c712 with a regression test. This change was incorrect. When we observe an unmodified refcount of zero, it's possible that some other thread brought this up, and then back down again. Bug: 490953577 Change-Id: I3248c5440ee7a6310774a85a728aa24e29c6a158 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/90587 Auto-Submit: David Benjamin <davidben@google.com> Reviewed-by: Rudolf Polzer <rpolzer@google.com> Commit-Queue: David Benjamin <davidben@google.com> Commit-Queue: Rudolf Polzer <rpolzer@google.com>
diff --git a/crypto/pool/pool.cc b/crypto/pool/pool.cc index ddc37fa..3c47143 100644 --- a/crypto/pool/pool.cc +++ b/crypto/pool/pool.cc
@@ -89,24 +89,24 @@ } void CryptoBuffer::UpRefInternal() { - // Note that, unlike standard ref-counting, it is possible that |references_| - // is zero and this function increases the reference count to one. See - // DecRefInternal below. + // This is safe in the case that |buf->pool| is NULL because it's just + // standard reference counting in that case. + // + // This is also safe if |buf->pool| is non-NULL because, if it were racing + // with |CRYPTO_BUFFER_free| then the two callers must have independent + // references already and so the reference count will never hit zero. CRYPTO_refcount_inc(&references_); } void CryptoBuffer::DecRefInternal() { - if (!CRYPTO_refcount_dec_and_test_zero(&references_)) { - return; - } - - if (pool_handle_ != nullptr) { - // Although the reference count is zero, there is still a weak reference - // from the pool. Some other thread may concurrently upgrade the weak - // reference to a strong reference. Synchronize with the pool and see if - // this has happened. + // If there is a pool, decrementing the refcount must synchronize with it. + if (pool_handle_ == nullptr) { + if (!CRYPTO_refcount_dec_and_test_zero(&references_)) { + return; + } + } else { MutexWriteLock lock(&pool_handle_->lock_); - if (references_.load() != 0) { + if (!CRYPTO_refcount_dec_and_test_zero(&references_)) { return; } @@ -127,10 +127,6 @@ (void)found; } } - - // The weak reference is now removed. When we unlock the pool handle, - // another thread cannot upgrade to a strong reference, so we can free the - // buffer. } this->~CryptoBuffer(); @@ -182,8 +178,6 @@ duplicate = nullptr; } if (duplicate != nullptr) { - // UpRef must be called under the lock. This converts the pool's weak - // reference to a strong reference. See CryptoBuffer::DecRefInternal. return UpRef(duplicate); } } @@ -201,8 +195,6 @@ duplicate = nullptr; } if (duplicate != nullptr) { - // UpRef must be called under the lock. This converts the pool's weak - // reference to a strong reference. See CryptoBuffer::DecRefInternal. return UpRef(duplicate); }
diff --git a/crypto/pool/pool_test.cc b/crypto/pool/pool_test.cc index f753b80..055bc11 100644 --- a/crypto/pool/pool_test.cc +++ b/crypto/pool/pool_test.cc
@@ -161,6 +161,7 @@ // Race threads making pooled |CRYPTO_BUFFER|s. static const uint8_t kData[4] = {1, 2, 3, 4}; static const uint8_t kData2[3] = {4, 5, 6}; + static const uint8_t kData3[3] = {7, 8, 9}; UniquePtr<CRYPTO_BUFFER> buf, buf2, buf3; { std::thread thread([&] { @@ -228,6 +229,25 @@ buf = std::move(buf2); } + // Race two threads repeatedly creating and destroying a buffer, to race + // destruction with a strong/weak reference upgrade. + { + constexpr size_t kNumThreads = 2; + constexpr size_t kIterations = 1000; + std::vector<std::thread> threads; + for (size_t i = 0; i < kNumThreads; i++) { + threads.emplace_back([&] { + for (size_t j = 0; j < kIterations; j++) { + CRYPTO_BUFFER_free( + CRYPTO_BUFFER_new(kData3, sizeof(kData3), pool.get())); + } + }); + } + for (size_t i = 0; i < kNumThreads; i++) { + threads[i].join(); + } + } + // Finally, race the frees. { buf2 = UpRef(buf);