Handle CBB_cleanup on child CBBs more gracefully.

Child and root CBBs share a type, but are different kinds of things. C++
programmers sometimes mistakenly believe they should use ScopedCBB for
everything. This mostly works because we NULL cbb->child->base on flush,
making CBB_cleanup a no-op. This zeroing also skips the assert in
CBB_cleanup. (If we ran it unconditionally, CBB_zero + CBB_cleanup would
not work.)

However, if a CBB operation fails and a function returns early, the
child CBB is not cleared. ScopedCBB will then call CBB_cleanup which
trips the assert but, in release build, misbehaves.

Run the assert unconditionally and, when the assert fails, still behave
well. To make this work with CBB_zero, negate is_top_level to is_child,
so a flushed child CBB and a (presumably) root CBB in the zero state are
distinguishable.

Update-Note: Code that was using CBB wrong may trip an assert in debug builds.
Change-Id: Ifea7759e1d0331f2e727c59bbafa355d70fb9dba
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/35524
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/bytestring/cbb.c b/crypto/bytestring/cbb.c
index 7998a48..1ddc73c 100644
--- a/crypto/bytestring/cbb.c
+++ b/crypto/bytestring/cbb.c
@@ -44,7 +44,7 @@
   base->error = 0;
 
   cbb->base = base;
-  cbb->is_top_level = 1;
+  cbb->is_child = 0;
   return 1;
 }
 
@@ -76,11 +76,14 @@
 }
 
 void CBB_cleanup(CBB *cbb) {
-  if (cbb->base) {
-    // Only top-level |CBB|s are cleaned up. Child |CBB|s are non-owning. They
-    // are implicitly discarded when the parent is flushed or cleaned up.
-    assert(cbb->is_top_level);
+  // Child |CBB|s are non-owning. They are implicitly discarded and should not
+  // be used with |CBB_cleanup| or |ScopedCBB|.
+  assert(!cbb->is_child);
+  if (cbb->is_child) {
+    return;
+  }
 
+  if (cbb->base) {
     if (cbb->base->can_resize) {
       OPENSSL_free(cbb->base->buf);
     }
@@ -169,7 +172,7 @@
 }
 
 int CBB_finish(CBB *cbb, uint8_t **out_data, size_t *out_len) {
-  if (!cbb->is_top_level) {
+  if (cbb->is_child) {
     return 0;
   }
 
@@ -310,6 +313,7 @@
   OPENSSL_memset(prefix_bytes, 0, len_len);
   OPENSSL_memset(out_contents, 0, sizeof(CBB));
   out_contents->base = cbb->base;
+  out_contents->is_child = 1;
   cbb->child = out_contents;
   cbb->child->offset = offset;
   cbb->child->pending_len_len = len_len;
@@ -381,6 +385,7 @@
 
   OPENSSL_memset(out_contents, 0, sizeof(CBB));
   out_contents->base = cbb->base;
+  out_contents->is_child = 1;
   cbb->child = out_contents;
   cbb->child->offset = offset;
   cbb->child->pending_len_len = 1;
diff --git a/include/openssl/bytestring.h b/include/openssl/bytestring.h
index 75b8434..029c2be 100644
--- a/include/openssl/bytestring.h
+++ b/include/openssl/bytestring.h
@@ -345,9 +345,9 @@
   // length-prefix, or zero if no length-prefix is pending.
   uint8_t pending_len_len;
   char pending_is_asn1;
-  // is_top_level is true iff this is a top-level |CBB| (as opposed to a child
+  // is_child is true iff this is a child |CBB| (as opposed to a top-level
   // |CBB|). Top-level objects are valid arguments for |CBB_finish|.
-  char is_top_level;
+  char is_child;
 };
 
 // CBB_zero sets an uninitialised |cbb| to the zero state. It must be