Fix the docs for and test BIO_free's return value

Also remove the TODO(fork) to drop BIO_free_all. Too much third-party
code relies on it.

Bug: 485657226
Change-Id: I470d90e76548cb43f514d607a0ba5a50511b0978
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/89707
Auto-Submit: David Benjamin <davidben@google.com>
Reviewed-by: Xiangfei Ding <xfding@google.com>
Commit-Queue: Xiangfei Ding <xfding@google.com>
diff --git a/crypto/bio/bio_test.cc b/crypto/bio/bio_test.cc
index 2de44e4..aedf3aa 100644
--- a/crypto/bio/bio_test.cc
+++ b/crypto/bio/bio_test.cc
@@ -923,5 +923,36 @@
 
 INSTANTIATE_TEST_SUITE_P(All, BIOPairTest, testing::Values(false, true));
 
+// |BIO_free| returns whether the input |BIO| was shared.
+TEST(BIOTest, BIOFreeReturnValue) {
+  BIO *bio = BIO_new_mem_buf(nullptr, 0);
+  ASSERT_TRUE(bio);
+  BIO_up_ref(bio);
+  BIO_up_ref(bio);
+
+  // |BIO_free| should return one when the last reference is dropped.
+  EXPECT_EQ(0, BIO_free(bio));
+  EXPECT_EQ(0, BIO_free(bio));
+  EXPECT_EQ(1, BIO_free(bio));
+
+  // |BIO_free| of nullptr vacuously returns one.
+  EXPECT_EQ(1, BIO_free(nullptr));
+}
+
+TEST(BIOTest, BIOFreeReturnValueChain) {
+  // We have no built-in filter BIOs, but |BIO_push| works with any |BIO|, so
+  // just chain memory |BIO|s, even though it does nothing.
+  UniquePtr<BIO> bio1(BIO_new_mem_buf(nullptr, 0));
+  ASSERT_TRUE(bio1);
+  UniquePtr<BIO> bio2(BIO_new_mem_buf(nullptr, 0));
+  ASSERT_TRUE(bio2);
+  BIO_push(bio1.get(), UpRef(bio2).release());
+
+  // |bio1| now owns a copy of |bio2|, but it is still shared with the |bio2|
+  // pointer. This causes |BIO_free| to return zero.
+  // TODO(crbug.com/485657226): It should return one.
+  EXPECT_EQ(0, BIO_free(bio1.release()));
+}
+
 }  // namespace
 BSSL_NAMESPACE_END
diff --git a/include/openssl/bio.h b/include/openssl/bio.h
index b19607a..59ca6b8 100644
--- a/include/openssl/bio.h
+++ b/include/openssl/bio.h
@@ -42,10 +42,22 @@
 
 // BIO_free decrements the reference count of |bio|. If the reference count
 // drops to zero, it calls the destroy callback, if present, on the method and
-// frees |bio| itself. It then repeats that for the next BIO in the chain, if
-// any.
+// frees |bio| itself. If |bio| is part of a chain (see |BIO_push|), this will
+// also free the next |BIO| in the chain, and so on.
 //
-// It returns one on success or zero otherwise.
+// It returns one if |bio| was NULL or freed. It returns zero if |bio| was
+// shared and some other owner still owns a reference count to it.
+//
+// TODO(crbug.com/485657226): Currently, if any |BIO| in the chain is shared,
+// this function will also return zero. It should only return zero when the
+// input |BIO| is shared.
+//
+// WARNING: Do not use the return value. Returning zero is not a sign of an
+// error, nor an indication to retry the operation. |BIO| is a reference-counted
+// type. A given |BIO| object may be shared between multiple parts of an
+// application. To correctly track the reference count, without leaks or
+// use-after-free, each part of the application must release only the reference
+// counts it owns.
 OPENSSL_EXPORT int BIO_free(BIO *bio);
 
 // BIO_vfree performs the same actions as |BIO_free|, but has a void return
@@ -238,11 +250,6 @@
 // no such BIO.
 OPENSSL_EXPORT BIO *BIO_next(BIO *bio);
 
-// BIO_free_all calls |BIO_free|.
-//
-// TODO(fork): update callers and remove.
-OPENSSL_EXPORT void BIO_free_all(BIO *bio);
-
 // BIO_find_type walks a chain of BIOs and returns the first that matches
 // |type|, which is one of the |BIO_TYPE_*| values.
 OPENSSL_EXPORT BIO *BIO_find_type(BIO *bio, int type);
@@ -778,6 +785,10 @@
 
 // Deprecated functions.
 
+// BIO_free_all calls |BIO_free|. Code that targets BoringSSL does not need to
+// call a separate free function for |BIO|s that are part of a chain.
+OPENSSL_EXPORT void BIO_free_all(BIO *bio);
+
 typedef BIO_info_cb bio_info_cb;
 
 // BIO_f_base64 returns a filter |BIO| that base64-encodes data written into