Add a basic threading test for X509 verification.

X509 verification currently lazily fills in some fields on a lock. Test
that it works correctly. Confirmed that TSan can catch data races
intentionally patched into v3_purp.c.

Change-Id: Ia0e8d81bb6ba4b9ade1a47edcb48404902f4ae8c
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/55745
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: Bob Beck <bbe@google.com>
Auto-Submit: David Benjamin <davidben@google.com>
diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc
index 5e089d4..e5e1b84 100644
--- a/crypto/x509/x509_test.cc
+++ b/crypto/x509/x509_test.cc
@@ -37,6 +37,10 @@
 #include "../test/test_util.h"
 #include "../x509v3/internal.h"
 
+#if defined(OPENSSL_THREADS)
+#include <thread>
+#endif
+
 
 std::string GetTestData(const char *path);
 
@@ -1247,6 +1251,31 @@
   }
 }
 
+#if defined(OPENSSL_THREADS)
+// Verifying the same |X509| objects on two threads should be safe.
+TEST(X509Test, VerifyThreads) {
+  bssl::UniquePtr<X509> root(CertFromPEM(kRootCAPEM));
+  bssl::UniquePtr<X509> intermediate(CertFromPEM(kIntermediatePEM));
+  bssl::UniquePtr<X509> leaf(CertFromPEM(kLeafPEM));
+  ASSERT_TRUE(root);
+  ASSERT_TRUE(intermediate);
+  ASSERT_TRUE(leaf);
+
+  const size_t kNumThreads = 10;
+  std::vector<std::thread> threads;
+  for (size_t i = 0; i < kNumThreads; i++) {
+    threads.emplace_back([&] {
+      EXPECT_EQ(X509_V_OK,
+                Verify(leaf.get(), {root.get()}, {intermediate.get()},
+                       /*crls=*/{}));
+    });
+  }
+  for (auto &thread : threads) {
+    thread.join();
+  }
+}
+#endif  // OPENSSL_THREADS
+
 static const char kHostname[] = "example.com";
 static const char kWrongHostname[] = "example2.com";
 static const char kEmail[] = "test@example.com";