crypto/bio: Don't call method->destroy if method->create failed

Update-Note: If create callback of BIO_METHOD fails, the destroy method will no
longer be called. This means:

- The create method is responsible for cleaning up any state if it fails.

- The destroy method no longer needs to consider partial states when create
  fails.

This is a slight behavior change to BIO_METHOD, but restores our previous
behavior and aligns with OpenSSL. This reverts a regression
introduced by
https://boringssl-review.googlesource.com/c/boringssl/+/89189.

Signed-off-by: Xiangfei Ding <xfding@google.com>
Change-Id: I0155df499ff6ed1169943d49d7300f4f6a6a6964
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/104048
Presubmit-BoringSSL-Verified: boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com <boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/crypto/bio/bio.cc b/crypto/bio/bio.cc
index 28c3f4b..7db3793 100644
--- a/crypto/bio/bio.cc
+++ b/crypto/bio/bio.cc
@@ -15,7 +15,6 @@
 #include <openssl/bio.h>
 
 #include <assert.h>
-#include <errno.h>
 #include <limits.h>
 #include <string.h>
 
@@ -47,6 +46,8 @@
   }
 
   if (method->create != nullptr && !method->create(ret.get())) {
+    // Matching OpenSSL, don't call `destroy` if `create` fails.
+    ret->method = nullptr;
     return nullptr;
   }
 
@@ -55,7 +56,8 @@
 
 Bio::~Bio() {
   BIO *next = BIO_pop(this);
-  if (method->destroy != nullptr) {
+  // `method` may be null if `create` failed.
+  if (method != nullptr && method->destroy != nullptr) {
     method->destroy(this);
   }
   CRYPTO_free_ex_data(&g_ex_data_class, &ex_data);
diff --git a/crypto/bio/bio_test.cc b/crypto/bio/bio_test.cc
index ab58fbb..c2506d6 100644
--- a/crypto/bio/bio_test.cc
+++ b/crypto/bio/bio_test.cc
@@ -1577,5 +1577,21 @@
   }
 }
 
+TEST(BIOTest, CreateFailure) {
+  UniquePtr<BIO_METHOD> method(BIO_meth_new(0, nullptr));
+  ASSERT_TRUE(method);
+  ASSERT_TRUE(
+      BIO_meth_set_create(method.get(), [](BIO *) -> int { return 0; }));
+  ASSERT_TRUE(BIO_meth_set_destroy(method.get(), [](BIO *) -> int {
+    ADD_FAILURE() << "destroy must not be called if create failed";
+    return 0;
+  }));
+
+  // If `create` fails, `BIO_new` should return nullptr without calling
+  // `destroy`.
+  UniquePtr<BIO> bio(BIO_new(method.get()));
+  EXPECT_FALSE(bio);
+}
+
 }  // namespace
 BSSL_NAMESPACE_END