Reapply "Migrate Bio to RefCounted."

This reverts commit 75186f6b2aa6c18b9ae91923eaa005657be33c9b. This
relands the change with two changes:

1. The return value of BIO_free is preserved.

2. We call BIO_pop before destroying the BIO, not after. This is a
   no-op, but is less risky given the history of this code. In OpenSSL,
   BIO_pop notifies the BIO_METHOD with BIO_CTRL_POP. We don't currently
   have this but, if we ever do, let's not risk some big mishap.

Bug: 485657226
Change-Id: I4c55566d3325e95825b003855911f56fdd3f5a24
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/89709
Reviewed-by: Xiangfei Ding <xfding@google.com>
Commit-Queue: Xiangfei Ding <xfding@google.com>
Auto-Submit: David Benjamin <davidben@google.com>
diff --git a/crypto/bio/bio.cc b/crypto/bio/bio.cc
index be6f65b..c2a825f 100644
--- a/crypto/bio/bio.cc
+++ b/crypto/bio/bio.cc
@@ -33,54 +33,43 @@
 static CRYPTO_EX_DATA_CLASS g_ex_data_class =
     CRYPTO_EX_DATA_CLASS_INIT_WITH_APP_DATA;
 
+Bio::Bio(const BIO_METHOD *m) : RefCounted(CheckSubClass()), method(m) {
+  CRYPTO_new_ex_data(&ex_data);
+}
+
 BIO *BIO_new(const BIO_METHOD *method) {
-  Bio *ret = NewZeroed<Bio>();
+  UniquePtr<Bio> ret(New<Bio>(method));
   if (ret == nullptr) {
     return nullptr;
   }
 
-  ret->method = method;
-  ret->shutdown = 1;
-  ret->references = 1;
-  CRYPTO_new_ex_data(&ret->ex_data);
-
-  if (method->create != nullptr && !method->create(ret)) {
-    Delete(ret);
+  if (method->create != nullptr && !method->create(ret.get())) {
     return nullptr;
   }
 
-  return ret;
+  return ret.release();
+}
+
+Bio::~Bio() {
+  BIO *next = BIO_pop(this);
+  if (method != nullptr && method->destroy != nullptr) {
+    method->destroy(this);
+  }
+  CRYPTO_free_ex_data(&g_ex_data_class, &ex_data);
+  BIO_free(next);
 }
 
 int BIO_free(BIO *bio) {
-  auto *impl = FromOpaque(bio);
-  bool is_input = true;
-  Bio *next_bio;
-  for (; impl != nullptr; impl = next_bio) {
-    if (!CRYPTO_refcount_dec_and_test_zero(&impl->references)) {
-      // |impl| was shared. If |impl| was the input, report to the caller
-      // that the object was shared. If it was a downstream reference, the input
-      // was unshared and we continue to return that |bio| was freed.
-      return !is_input;
-    }
-
-    next_bio = FromOpaque(BIO_pop(impl));
-
-    if (impl->method != nullptr && impl->method->destroy != nullptr) {
-      impl->method->destroy(impl);
-    }
-
-    CRYPTO_free_ex_data(&g_ex_data_class, &impl->ex_data);
-    Delete(impl);
-    is_input = false;
+  if (bio == nullptr) {
+    return 1;
   }
-  return 1;
+  auto *impl = FromOpaque(bio);
+  return impl->DecRefInternal();
 }
 
 int BIO_up_ref(BIO *bio) {
   auto *impl = FromOpaque(bio);
-
-  CRYPTO_refcount_inc(&impl->references);
+  impl->UpRefInternal();
   return 1;
 }
 
diff --git a/crypto/bio/internal.h b/crypto/bio/internal.h
index 6c9e02e..77cd8a8 100644
--- a/crypto/bio/internal.h
+++ b/crypto/bio/internal.h
@@ -20,6 +20,7 @@
 #include <openssl/ex_data.h>
 
 #include "../internal.h"
+#include "../mem_internal.h"
 
 #if !defined(OPENSSL_NO_SOCK)
 #if !defined(OPENSSL_WINDOWS)
@@ -52,8 +53,10 @@
 
 BSSL_NAMESPACE_BEGIN
 
-class Bio : public bio_st {
+class Bio : public bio_st, public RefCounted<Bio> {
  public:
+  explicit Bio(const BIO_METHOD *m);
+
   const BIO_METHOD *method;
   CRYPTO_EX_DATA ex_data;
 
@@ -61,23 +64,26 @@
   // integrated into |flags|, to save memory.
 
   // init is non-zero if this |BIO| has been initialised.
-  int init;
+  int init = 0;
   // shutdown is often used by specific |BIO_METHOD|s to determine whether
   // they own some underlying resource. This flag can often be controlled by
   // |BIO_set_close|. For example, whether an fd BIO closes the underlying fd
   // when it, itself, is closed.
-  int shutdown;
-  int flags;
-  int retry_reason;
+  int shutdown = 1;
+  int flags = 0;
+  int retry_reason = 0;
   // num is a BIO-specific value. For example, in fd BIOs it's used to store a
   // file descriptor.
-  int num;
-  bssl::CRYPTO_refcount_t references;
-  void *ptr;
+  int num = 0;
+  void *ptr = nullptr;
   // next_bio points to the next |BIO| in a chain. This |BIO| owns a reference
   // to |next_bio|.
-  Bio *next_bio;  // used by filter BIOs
-  uint64_t num_read, num_write;
+  Bio *next_bio = nullptr;  // used by filter BIOs
+  uint64_t num_read = 0, num_write = 0;
+
+ private:
+  friend RefCounted;
+  ~Bio();
 };
 
 #if !defined(OPENSSL_NO_SOCK)
diff --git a/crypto/mem_internal.h b/crypto/mem_internal.h
index 14254ec..5cc5e5c 100644
--- a/crypto/mem_internal.h
+++ b/crypto/mem_internal.h
@@ -139,14 +139,17 @@
 
   // These methods are intentionally named differently from `bssl::UpRef` to
   // avoid a collision. Only the implementations of `FOO_up_ref` and `FOO_free`
-  // should call these.
+  // should call these. |DecRefInternal| returns true if the object was freed
+  // and false if there are still references.
   void UpRefInternal() { CRYPTO_refcount_inc(&references_); }
-  void DecRefInternal() {
+  bool DecRefInternal() {
     if (CRYPTO_refcount_dec_and_test_zero(&references_)) {
       Derived *d = static_cast<Derived *>(this);
       d->~Derived();
       OPENSSL_free(d);
+      return true;
     }
+    return false;
   }
 
  protected: