Add some TSan coverage of CRYPTO_BUFFER. There were some subtleties in this one. I'm not sure if TSan covers it all, but it's better than nothing. Change-Id: I239e3aee2fea84caa2e48f555d08c6d89f430402 Reviewed-on: https://boringssl-review.googlesource.com/29927 Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/pool/pool_test.cc b/crypto/pool/pool_test.cc index 3310fc3..62fb17d 100644 --- a/crypto/pool/pool_test.cc +++ b/crypto/pool/pool_test.cc
@@ -18,6 +18,11 @@ #include "../test/test_util.h" +#if !defined(OPENSSL_NO_THREADS) +#include <chrono> +#include <thread> +#endif + TEST(PoolTest, Unpooled) { static const uint8_t kData[4] = {1, 2, 3, 4}; @@ -55,3 +60,90 @@ EXPECT_EQ(buf.get(), buf2.get()) << "CRYPTO_BUFFER_POOL did not dedup data."; } + +#if !defined(OPENSSL_NO_THREADS) +TEST(PoolTest, Threads) { + bssl::UniquePtr<CRYPTO_BUFFER_POOL> pool(CRYPTO_BUFFER_POOL_new()); + ASSERT_TRUE(pool); + + // 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}; + bssl::UniquePtr<CRYPTO_BUFFER> buf, buf2, buf3; + { + std::thread thread([&] { + buf.reset(CRYPTO_BUFFER_new(kData, sizeof(kData), pool.get())); + }); + std::thread thread2([&] { + buf2.reset(CRYPTO_BUFFER_new(kData, sizeof(kData), pool.get())); + }); + buf3.reset(CRYPTO_BUFFER_new(kData2, sizeof(kData2), pool.get())); + thread.join(); + thread2.join(); + } + + ASSERT_TRUE(buf); + ASSERT_TRUE(buf2); + ASSERT_TRUE(buf3); + EXPECT_EQ(buf.get(), buf2.get()) << "CRYPTO_BUFFER_POOL did not dedup data."; + EXPECT_NE(buf.get(), buf3.get()) + << "CRYPTO_BUFFER_POOL incorrectly deduped data."; + EXPECT_EQ(Bytes(kData), + Bytes(CRYPTO_BUFFER_data(buf.get()), CRYPTO_BUFFER_len(buf.get()))); + EXPECT_EQ(Bytes(kData2), Bytes(CRYPTO_BUFFER_data(buf3.get()), + CRYPTO_BUFFER_len(buf3.get()))); + + // Reference-counting of |CRYPTO_BUFFER| interacts with pooling. Race an + // increment and free. + { + bssl::UniquePtr<CRYPTO_BUFFER> buf_ref; + std::thread thread([&] { buf_ref = bssl::UpRef(buf); }); + buf2.reset(); + thread.join(); + } + + // |buf|'s data is still valid. + EXPECT_EQ(Bytes(kData), Bytes(CRYPTO_BUFFER_data(buf.get()), + CRYPTO_BUFFER_len(buf.get()))); + + // Race a thread re-creating the |CRYPTO_BUFFER| with another thread freeing + // it. Do this twice with sleeps so ThreadSanitizer can observe two different + // interleavings. Ideally we would run this test under a tool that could + // search all interleavings. + { + std::thread thread([&] { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + buf.reset(); + }); + buf2.reset(CRYPTO_BUFFER_new(kData, sizeof(kData), pool.get())); + thread.join(); + + ASSERT_TRUE(buf2); + EXPECT_EQ(Bytes(kData), Bytes(CRYPTO_BUFFER_data(buf2.get()), + CRYPTO_BUFFER_len(buf2.get()))); + buf = std::move(buf2); + } + + { + std::thread thread([&] { buf.reset(); }); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + buf2.reset(CRYPTO_BUFFER_new(kData, sizeof(kData), pool.get())); + thread.join(); + + ASSERT_TRUE(buf2); + EXPECT_EQ(Bytes(kData), Bytes(CRYPTO_BUFFER_data(buf2.get()), + CRYPTO_BUFFER_len(buf2.get()))); + buf = std::move(buf2); + } + + // Finally, race the frees. + { + buf2 = bssl::UpRef(buf); + std::thread thread([&] { buf.reset(); }); + std::thread thread2([&] { buf3.reset(); }); + buf2.reset(); + thread.join(); + thread2.join(); + } +} +#endif