external/boringssl: Sync to ba94746eb2b4b59a0eb72047e4ca2d2d54454c87.

This includes the following changes:

https://boringssl.googlesource.com/boringssl/+log/7f8c553d7f4db0a6ce727f2986d41bf8fe8ec4bf..ba94746eb2b4b59a0eb72047e4ca2d2d54454c87

Test: BoringSSL CTS Presubmits
Change-Id: I5283ca8ec80f4abbc2543fece2ecf2b33240c6e4
diff --git a/src/ssl/d1_both.cc b/src/ssl/d1_both.cc
index 798deb0..c219f5a 100644
--- a/src/ssl/d1_both.cc
+++ b/src/ssl/d1_both.cc
@@ -144,23 +144,18 @@
 
 // Receiving handshake messages.
 
-static void dtls1_hm_fragment_free(hm_fragment *frag) {
-  if (frag == NULL) {
-    return;
-  }
-  OPENSSL_free(frag->data);
-  OPENSSL_free(frag->reassembly);
-  OPENSSL_free(frag);
+hm_fragment::~hm_fragment() {
+  OPENSSL_free(data);
+  OPENSSL_free(reassembly);
 }
 
-static hm_fragment *dtls1_hm_fragment_new(const struct hm_header_st *msg_hdr) {
+static UniquePtr<hm_fragment> dtls1_hm_fragment_new(
+    const struct hm_header_st *msg_hdr) {
   ScopedCBB cbb;
-  hm_fragment *frag = (hm_fragment *)OPENSSL_malloc(sizeof(hm_fragment));
-  if (frag == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-    return NULL;
+  UniquePtr<hm_fragment> frag = MakeUnique<hm_fragment>();
+  if (!frag) {
+    return nullptr;
   }
-  OPENSSL_memset(frag, 0, sizeof(hm_fragment));
   frag->type = msg_hdr->type;
   frag->seq = msg_hdr->seq;
   frag->msg_len = msg_hdr->msg_len;
@@ -170,7 +165,7 @@
       (uint8_t *)OPENSSL_malloc(DTLS1_HM_HEADER_LENGTH + msg_hdr->msg_len);
   if (frag->data == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-    goto err;
+    return nullptr;
   }
 
   if (!CBB_init_fixed(cbb.get(), frag->data, DTLS1_HM_HEADER_LENGTH) ||
@@ -181,7 +176,7 @@
       !CBB_add_u24(cbb.get(), msg_hdr->msg_len) ||
       !CBB_finish(cbb.get(), NULL, NULL)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-    goto err;
+    return nullptr;
   }
 
   // If the handshake message is empty, |frag->reassembly| is NULL.
@@ -189,22 +184,18 @@
     // Initialize reassembly bitmask.
     if (msg_hdr->msg_len + 7 < msg_hdr->msg_len) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
-      goto err;
+      return nullptr;
     }
     size_t bitmask_len = (msg_hdr->msg_len + 7) / 8;
     frag->reassembly = (uint8_t *)OPENSSL_malloc(bitmask_len);
     if (frag->reassembly == NULL) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-      goto err;
+      return nullptr;
     }
     OPENSSL_memset(frag->reassembly, 0, bitmask_len);
   }
 
   return frag;
-
-err:
-  dtls1_hm_fragment_free(frag);
-  return NULL;
 }
 
 // bit_range returns a |uint8_t| with bits |start|, inclusive, to |end|,
@@ -262,8 +253,8 @@
 // dtls1_is_current_message_complete returns whether the current handshake
 // message is complete.
 static bool dtls1_is_current_message_complete(const SSL *ssl) {
-  hm_fragment *frag = ssl->d1->incoming_messages[ssl->d1->handshake_read_seq %
-                                                 SSL_MAX_HANDSHAKE_FLIGHT];
+  size_t idx = ssl->d1->handshake_read_seq % SSL_MAX_HANDSHAKE_FLIGHT;
+  hm_fragment *frag = ssl->d1->incoming_messages[idx].get();
   return frag != NULL && frag->reassembly == NULL;
 }
 
@@ -280,7 +271,7 @@
   }
 
   size_t idx = msg_hdr->seq % SSL_MAX_HANDSHAKE_FLIGHT;
-  hm_fragment *frag = ssl->d1->incoming_messages[idx];
+  hm_fragment *frag = ssl->d1->incoming_messages[idx].get();
   if (frag != NULL) {
     assert(frag->seq == msg_hdr->seq);
     // The new fragment must be compatible with the previous fragments from this
@@ -295,13 +286,12 @@
   }
 
   // This is the first fragment from this message.
-  frag = dtls1_hm_fragment_new(msg_hdr);
-  if (frag == NULL) {
+  ssl->d1->incoming_messages[idx] = dtls1_hm_fragment_new(msg_hdr);
+  if (!ssl->d1->incoming_messages[idx]) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
     return NULL;
   }
-  ssl->d1->incoming_messages[idx] = frag;
-  return frag;
+  return ssl->d1->incoming_messages[idx].get();
 }
 
 ssl_open_record_t dtls1_open_handshake(SSL *ssl, size_t *out_consumed,
@@ -420,8 +410,8 @@
     return false;
   }
 
-  hm_fragment *frag = ssl->d1->incoming_messages[ssl->d1->handshake_read_seq %
-                                                 SSL_MAX_HANDSHAKE_FLIGHT];
+  size_t idx = ssl->d1->handshake_read_seq % SSL_MAX_HANDSHAKE_FLIGHT;
+  hm_fragment *frag = ssl->d1->incoming_messages[idx].get();
   out->type = frag->type;
   CBS_init(&out->body, frag->data + DTLS1_HM_HEADER_LENGTH, frag->msg_len);
   CBS_init(&out->raw, frag->data, DTLS1_HM_HEADER_LENGTH + frag->msg_len);
@@ -437,8 +427,7 @@
   assert(ssl->s3->has_message);
   assert(dtls1_is_current_message_complete(ssl));
   size_t index = ssl->d1->handshake_read_seq % SSL_MAX_HANDSHAKE_FLIGHT;
-  dtls1_hm_fragment_free(ssl->d1->incoming_messages[index]);
-  ssl->d1->incoming_messages[index] = NULL;
+  ssl->d1->incoming_messages[index].reset();
   ssl->d1->handshake_read_seq++;
   ssl->s3->has_message = false;
   // If we previously sent a flight, mark it as having a reply, so
@@ -448,13 +437,6 @@
   }
 }
 
-void dtls_clear_incoming_messages(SSL *ssl) {
-  for (size_t i = 0; i < SSL_MAX_HANDSHAKE_FLIGHT; i++) {
-    dtls1_hm_fragment_free(ssl->d1->incoming_messages[i]);
-    ssl->d1->incoming_messages[i] = NULL;
-  }
-}
-
 bool dtls_has_unprocessed_handshake_data(const SSL *ssl) {
   if (ssl->d1->has_change_cipher_spec) {
     return true;
@@ -467,7 +449,7 @@
       assert(dtls1_is_current_message_complete(ssl));
       continue;
     }
-    if (ssl->d1->incoming_messages[i] != NULL) {
+    if (ssl->d1->incoming_messages[i] != nullptr) {
       return true;
     }
   }
@@ -510,10 +492,14 @@
 
 // Sending handshake messages.
 
+void DTLS_OUTGOING_MESSAGE::Clear() {
+  OPENSSL_free(data);
+  data = nullptr;
+}
+
 void dtls_clear_outgoing_messages(SSL *ssl) {
   for (size_t i = 0; i < ssl->d1->outgoing_messages_len; i++) {
-    OPENSSL_free(ssl->d1->outgoing_messages[i].data);
-    ssl->d1->outgoing_messages[i].data = NULL;
+    ssl->d1->outgoing_messages[i].Clear();
   }
   ssl->d1->outgoing_messages_len = 0;
   ssl->d1->outgoing_written = 0;
@@ -552,7 +538,7 @@
 
 // add_outgoing adds a new handshake message or ChangeCipherSpec to the current
 // outgoing flight. It returns true on success and false on error.
-static bool add_outgoing(SSL *ssl, int is_ccs, Array<uint8_t> data) {
+static bool add_outgoing(SSL *ssl, bool is_ccs, Array<uint8_t> data) {
   if (ssl->d1->outgoing_messages_complete) {
     // If we've begun writing a new flight, we received the peer flight. Discard
     // the timer and the our flight.
@@ -594,11 +580,11 @@
 }
 
 bool dtls1_add_message(SSL *ssl, Array<uint8_t> data) {
-  return add_outgoing(ssl, 0 /* handshake */, std::move(data));
+  return add_outgoing(ssl, false /* handshake */, std::move(data));
 }
 
 bool dtls1_add_change_cipher_spec(SSL *ssl) {
-  return add_outgoing(ssl, 1 /* ChangeCipherSpec */, Array<uint8_t>());
+  return add_outgoing(ssl, true /* ChangeCipherSpec */, Array<uint8_t>());
 }
 
 bool dtls1_add_alert(SSL *ssl, uint8_t level, uint8_t desc) {
@@ -808,14 +794,14 @@
       // Retry this packet the next time around.
       ssl->d1->outgoing_written = old_written;
       ssl->d1->outgoing_offset = old_offset;
-      ssl->rwstate = SSL_WRITING;
+      ssl->s3->rwstate = SSL_WRITING;
       ret = bio_ret;
       goto err;
     }
   }
 
   if (BIO_flush(ssl->wbio) <= 0) {
-    ssl->rwstate = SSL_WRITING;
+    ssl->s3->rwstate = SSL_WRITING;
     goto err;
   }
 
diff --git a/src/ssl/d1_lib.cc b/src/ssl/d1_lib.cc
index 38bc5be..eff06ee 100644
--- a/src/ssl/d1_lib.cc
+++ b/src/ssl/d1_lib.cc
@@ -78,18 +78,24 @@
 // before failing the DTLS handshake.
 #define DTLS1_MAX_TIMEOUTS                     12
 
+DTLS1_STATE::DTLS1_STATE()
+    : has_change_cipher_spec(false),
+      outgoing_messages_complete(false),
+      flight_has_reply(false) {}
+
+DTLS1_STATE::~DTLS1_STATE() {}
+
 bool dtls1_new(SSL *ssl) {
   if (!ssl3_new(ssl)) {
     return false;
   }
-  DTLS1_STATE *d1 = (DTLS1_STATE *)OPENSSL_malloc(sizeof *d1);
-  if (d1 == NULL) {
+  UniquePtr<DTLS1_STATE> d1 = MakeUnique<DTLS1_STATE>();
+  if (!d1) {
     ssl3_free(ssl);
     return false;
   }
-  OPENSSL_memset(d1, 0, sizeof *d1);
 
-  ssl->d1 = d1;
+  ssl->d1 = d1.release();
 
   // Set the version to the highest supported version.
   //
@@ -103,15 +109,11 @@
 void dtls1_free(SSL *ssl) {
   ssl3_free(ssl);
 
-  if (ssl == NULL || ssl->d1 == NULL) {
+  if (ssl == NULL) {
     return;
   }
 
-  dtls_clear_incoming_messages(ssl);
-  dtls_clear_outgoing_messages(ssl);
-  Delete(ssl->d1->last_aead_write_ctx);
-
-  OPENSSL_free(ssl->d1);
+  Delete(ssl->d1);
   ssl->d1 = NULL;
 }
 
diff --git a/src/ssl/d1_pkt.cc b/src/ssl/d1_pkt.cc
index c6be93d..d29a5c2 100644
--- a/src/ssl/d1_pkt.cc
+++ b/src/ssl/d1_pkt.cc
@@ -187,8 +187,8 @@
   return ssl_open_record_success;
 }
 
-int dtls1_write_app_data(SSL *ssl, bool *out_needs_handshake,
-                         const uint8_t *buf, int len) {
+int dtls1_write_app_data(SSL *ssl, bool *out_needs_handshake, const uint8_t *in,
+                         int len) {
   assert(!SSL_in_init(ssl));
   *out_needs_handshake = false;
 
@@ -211,7 +211,7 @@
     return 0;
   }
 
-  int ret = dtls1_write_record(ssl, SSL3_RT_APPLICATION_DATA, buf, (size_t)len,
+  int ret = dtls1_write_record(ssl, SSL3_RT_APPLICATION_DATA, in, (size_t)len,
                                dtls1_use_current_epoch);
   if (ret <= 0) {
     return ret;
@@ -219,29 +219,29 @@
   return len;
 }
 
-int dtls1_write_record(SSL *ssl, int type, const uint8_t *buf, size_t len,
+int dtls1_write_record(SSL *ssl, int type, const uint8_t *in, size_t len,
                        enum dtls1_use_epoch_t use_epoch) {
+  SSLBuffer *buf = &ssl->s3->write_buffer;
   assert(len <= SSL3_RT_MAX_PLAIN_LENGTH);
   // There should never be a pending write buffer in DTLS. One can't write half
   // a datagram, so the write buffer is always dropped in
   // |ssl_write_buffer_flush|.
-  assert(!ssl_write_buffer_is_pending(ssl));
+  assert(buf->empty());
 
   if (len > SSL3_RT_MAX_PLAIN_LENGTH) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return -1;
   }
 
-  size_t max_out = len + SSL_max_seal_overhead(ssl);
-  uint8_t *out;
   size_t ciphertext_len;
-  if (!ssl_write_buffer_init(ssl, &out, max_out) ||
-      !dtls_seal_record(ssl, out, &ciphertext_len, max_out, type, buf, len,
-                        use_epoch)) {
-    ssl_write_buffer_clear(ssl);
+  if (!buf->EnsureCap(ssl_seal_align_prefix_len(ssl),
+                      len + SSL_max_seal_overhead(ssl)) ||
+      !dtls_seal_record(ssl, buf->remaining().data(), &ciphertext_len,
+                        buf->remaining().size(), type, in, len, use_epoch)) {
+    buf->Clear();
     return -1;
   }
-  ssl_write_buffer_set_len(ssl, ciphertext_len);
+  buf->DidWrite(ciphertext_len);
 
   int ret = ssl_write_buffer_flush(ssl);
   if (ret <= 0) {
diff --git a/src/ssl/dtls_method.cc b/src/ssl/dtls_method.cc
index fab19be..91a955f 100644
--- a/src/ssl/dtls_method.cc
+++ b/src/ssl/dtls_method.cc
@@ -94,8 +94,7 @@
   OPENSSL_memset(&ssl->d1->bitmap, 0, sizeof(ssl->d1->bitmap));
   OPENSSL_memset(ssl->s3->read_sequence, 0, sizeof(ssl->s3->read_sequence));
 
-  Delete(ssl->s3->aead_read_ctx);
-  ssl->s3->aead_read_ctx = aead_ctx.release();
+  ssl->s3->aead_read_ctx = std::move(aead_ctx);
   return true;
 }
 
@@ -106,9 +105,8 @@
                  sizeof(ssl->s3->write_sequence));
   OPENSSL_memset(ssl->s3->write_sequence, 0, sizeof(ssl->s3->write_sequence));
 
-  Delete(ssl->d1->last_aead_write_ctx);
-  ssl->d1->last_aead_write_ctx = ssl->s3->aead_write_ctx;
-  ssl->s3->aead_write_ctx = aead_ctx.release();
+  ssl->d1->last_aead_write_ctx = std::move(ssl->s3->aead_write_ctx);
+  ssl->s3->aead_write_ctx = std::move(aead_ctx);
   return true;
 }
 
diff --git a/src/ssl/dtls_record.cc b/src/ssl/dtls_record.cc
index a746640..5e795fa 100644
--- a/src/ssl/dtls_record.cc
+++ b/src/ssl/dtls_record.cc
@@ -275,10 +275,10 @@
                                             enum dtls1_use_epoch_t use_epoch) {
   if (use_epoch == dtls1_use_previous_epoch) {
     assert(ssl->d1->w_epoch >= 1);
-    return ssl->d1->last_aead_write_ctx;
+    return ssl->d1->last_aead_write_ctx.get();
   }
 
-  return ssl->s3->aead_write_ctx;
+  return ssl->s3->aead_write_ctx.get();
 }
 
 size_t dtls_max_seal_overhead(const SSL *ssl,
@@ -303,12 +303,12 @@
 
   // Determine the parameters for the current epoch.
   uint16_t epoch = ssl->d1->w_epoch;
-  SSLAEADContext *aead = ssl->s3->aead_write_ctx;
+  SSLAEADContext *aead = ssl->s3->aead_write_ctx.get();
   uint8_t *seq = ssl->s3->write_sequence;
   if (use_epoch == dtls1_use_previous_epoch) {
     assert(ssl->d1->w_epoch >= 1);
     epoch = ssl->d1->w_epoch - 1;
-    aead = ssl->d1->last_aead_write_ctx;
+    aead = ssl->d1->last_aead_write_ctx.get();
     seq = ssl->d1->last_write_sequence;
   }
 
diff --git a/src/ssl/handshake.cc b/src/ssl/handshake.cc
index 8531ca4..ed11484 100644
--- a/src/ssl/handshake.cc
+++ b/src/ssl/handshake.cc
@@ -149,17 +149,15 @@
   ssl->ctx->x509_method->hs_flush_cached_ca_names(this);
 }
 
-SSL_HANDSHAKE *ssl_handshake_new(SSL *ssl) {
+UniquePtr<SSL_HANDSHAKE> ssl_handshake_new(SSL *ssl) {
   UniquePtr<SSL_HANDSHAKE> hs = MakeUnique<SSL_HANDSHAKE>(ssl);
   if (!hs ||
       !hs->transcript.Init()) {
     return nullptr;
   }
-  return hs.release();
+  return hs;
 }
 
-void ssl_handshake_free(SSL_HANDSHAKE *hs) { Delete(hs); }
-
 bool ssl_check_message_type(SSL *ssl, const SSLMessage &msg, int type) {
   if (msg.type != type) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
@@ -282,7 +280,7 @@
 
 enum ssl_verify_result_t ssl_verify_peer_cert(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  const SSL_SESSION *prev_session = ssl->s3->established_session;
+  const SSL_SESSION *prev_session = ssl->s3->established_session.get();
   if (prev_session != NULL) {
     // If renegotiating, the server must not change the server certificate. See
     // https://mitls.org/pages/attacks/3SHAKE. We never resume on renegotiation,
@@ -505,10 +503,10 @@
         ssl_open_record_t ret;
         if (hs->wait == ssl_hs_read_change_cipher_spec) {
           ret = ssl_open_change_cipher_spec(ssl, &consumed, &alert,
-                                            ssl_read_buffer(ssl));
+                                            ssl->s3->read_buffer.span());
         } else {
-          ret =
-              ssl_open_handshake(ssl, &consumed, &alert, ssl_read_buffer(ssl));
+          ret = ssl_open_handshake(ssl, &consumed, &alert,
+                                   ssl->s3->read_buffer.span());
         }
         if (ret == ssl_open_record_error &&
             hs->wait == ssl_hs_read_server_hello) {
@@ -533,7 +531,7 @@
         if (retry) {
           continue;
         }
-        ssl_read_buffer_discard(ssl);
+        ssl->s3->read_buffer.DiscardConsumed();
         break;
       }
 
@@ -548,42 +546,42 @@
       }
 
       case ssl_hs_certificate_selection_pending:
-        ssl->rwstate = SSL_CERTIFICATE_SELECTION_PENDING;
+        ssl->s3->rwstate = SSL_CERTIFICATE_SELECTION_PENDING;
         hs->wait = ssl_hs_ok;
         return -1;
 
       case ssl_hs_x509_lookup:
-        ssl->rwstate = SSL_X509_LOOKUP;
+        ssl->s3->rwstate = SSL_X509_LOOKUP;
         hs->wait = ssl_hs_ok;
         return -1;
 
       case ssl_hs_channel_id_lookup:
-        ssl->rwstate = SSL_CHANNEL_ID_LOOKUP;
+        ssl->s3->rwstate = SSL_CHANNEL_ID_LOOKUP;
         hs->wait = ssl_hs_ok;
         return -1;
 
       case ssl_hs_private_key_operation:
-        ssl->rwstate = SSL_PRIVATE_KEY_OPERATION;
+        ssl->s3->rwstate = SSL_PRIVATE_KEY_OPERATION;
         hs->wait = ssl_hs_ok;
         return -1;
 
       case ssl_hs_pending_session:
-        ssl->rwstate = SSL_PENDING_SESSION;
+        ssl->s3->rwstate = SSL_PENDING_SESSION;
         hs->wait = ssl_hs_ok;
         return -1;
 
       case ssl_hs_pending_ticket:
-        ssl->rwstate = SSL_PENDING_TICKET;
+        ssl->s3->rwstate = SSL_PENDING_TICKET;
         hs->wait = ssl_hs_ok;
         return -1;
 
       case ssl_hs_certificate_verify:
-        ssl->rwstate = SSL_CERTIFICATE_VERIFY;
+        ssl->s3->rwstate = SSL_CERTIFICATE_VERIFY;
         hs->wait = ssl_hs_ok;
         return -1;
 
       case ssl_hs_early_data_rejected:
-        ssl->rwstate = SSL_EARLY_DATA_REJECTED;
+        ssl->s3->rwstate = SSL_EARLY_DATA_REJECTED;
         // Cause |SSL_write| to start failing immediately.
         hs->can_early_write = false;
         return -1;
diff --git a/src/ssl/handshake_client.cc b/src/ssl/handshake_client.cc
index 5466b56..ff8ebd8 100644
--- a/src/ssl/handshake_client.cc
+++ b/src/ssl/handshake_client.cc
@@ -1476,14 +1476,15 @@
 
   if (hs->next_proto_neg_seen) {
     static const uint8_t kZero[32] = {0};
-    size_t padding_len = 32 - ((ssl->s3->next_proto_negotiated_len + 2) % 32);
+    size_t padding_len =
+        32 - ((ssl->s3->next_proto_negotiated.size() + 2) % 32);
 
     ScopedCBB cbb;
     CBB body, child;
     if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_NEXT_PROTO) ||
         !CBB_add_u8_length_prefixed(&body, &child) ||
-        !CBB_add_bytes(&child, ssl->s3->next_proto_negotiated,
-                       ssl->s3->next_proto_negotiated_len) ||
+        !CBB_add_bytes(&child, ssl->s3->next_proto_negotiated.data(),
+                       ssl->s3->next_proto_negotiated.size()) ||
         !CBB_add_u8_length_prefixed(&body, &child) ||
         !CBB_add_bytes(&child, kZero, padding_len) ||
         !ssl_add_message_cbb(ssl, cbb.get())) {
@@ -1517,8 +1518,8 @@
   // False Start only for TLS 1.2 with an ECDHE+AEAD cipher and ALPN or NPN.
   return !SSL_is_dtls(ssl) &&
          SSL_version(ssl) == TLS1_2_VERSION &&
-         (ssl->s3->alpn_selected != NULL ||
-          ssl->s3->next_proto_negotiated != NULL) &&
+         (!ssl->s3->alpn_selected.empty() ||
+          !ssl->s3->next_proto_negotiated.empty()) &&
          hs->new_cipher->algorithm_mkey == SSL_kECDHE &&
          hs->new_cipher->algorithm_mac == SSL_AEAD;
 }
@@ -1664,18 +1665,16 @@
 
   ssl->method->on_handshake_complete(ssl);
 
-  SSL_SESSION_free(ssl->s3->established_session);
   if (ssl->session != NULL) {
     SSL_SESSION_up_ref(ssl->session);
-    ssl->s3->established_session = ssl->session;
+    ssl->s3->established_session.reset(ssl->session);
   } else {
     // We make a copy of the session in order to maintain the immutability
     // of the new established_session due to False Start. The caller may
     // have taken a reference to the temporary session.
     ssl->s3->established_session =
-        SSL_SESSION_dup(hs->new_session.get(), SSL_SESSION_DUP_ALL)
-        .release();
-    if (ssl->s3->established_session == NULL) {
+        SSL_SESSION_dup(hs->new_session.get(), SSL_SESSION_DUP_ALL);
+    if (!ssl->s3->established_session) {
       return ssl_hs_error;
     }
     // Renegotiations do not participate in session resumption.
diff --git a/src/ssl/handshake_server.cc b/src/ssl/handshake_server.cc
index 7b282d8..d346875 100644
--- a/src/ssl/handshake_server.cc
+++ b/src/ssl/handshake_server.cc
@@ -1392,8 +1392,7 @@
     return ssl_hs_error;
   }
 
-  if (!CBS_stow(&selected_protocol, &ssl->s3->next_proto_negotiated,
-                &ssl->s3->next_proto_negotiated_len)) {
+  if (!ssl->s3->next_proto_negotiated.CopyFrom(selected_protocol)) {
     return ssl_hs_error;
   }
 
@@ -1510,12 +1509,11 @@
     ssl->ctx->x509_method->session_clear(hs->new_session.get());
   }
 
-  SSL_SESSION_free(ssl->s3->established_session);
   if (ssl->session != NULL) {
     SSL_SESSION_up_ref(ssl->session);
-    ssl->s3->established_session = ssl->session;
+    ssl->s3->established_session.reset(ssl->session);
   } else {
-    ssl->s3->established_session = hs->new_session.release();
+    ssl->s3->established_session = std::move(hs->new_session);
     ssl->s3->established_session->not_resumable = 0;
   }
 
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index 174445a..7e1801a 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -746,10 +746,10 @@
 struct DTLS1_BITMAP {
   // map is a bit mask of the last 64 sequence numbers. Bit
   // |1<<i| corresponds to |max_seq_num - i|.
-  uint64_t map;
+  uint64_t map = 0;
   // max_seq_num is the largest sequence number seen so far as a 64-bit
   // integer.
-  uint64_t max_seq_num;
+  uint64_t max_seq_num = 0;
 };
 
 
@@ -998,9 +998,6 @@
 // in a handshake message for |ssl|.
 size_t ssl_max_handshake_message_len(const SSL *ssl);
 
-// dtls_clear_incoming_messages releases all buffered incoming messages.
-void dtls_clear_incoming_messages(SSL *ssl);
-
 // tls_can_accept_handshake_data returns whether |ssl| is able to accept more
 // data into handshake buffer.
 bool tls_can_accept_handshake_data(const SSL *ssl, uint8_t *out_alert);
@@ -1014,10 +1011,17 @@
 bool dtls_has_unprocessed_handshake_data(const SSL *ssl);
 
 struct DTLS_OUTGOING_MESSAGE {
-  uint8_t *data;
-  uint32_t len;
-  uint16_t epoch;
-  char is_ccs;
+  DTLS_OUTGOING_MESSAGE() {}
+  DTLS_OUTGOING_MESSAGE(const DTLS_OUTGOING_MESSAGE &) = delete;
+  DTLS_OUTGOING_MESSAGE &operator=(const DTLS_OUTGOING_MESSAGE &) = delete;
+  ~DTLS_OUTGOING_MESSAGE() { Clear(); }
+
+  void Clear();
+
+  uint8_t *data = nullptr;
+  uint32_t len = 0;
+  uint16_t epoch = 0;
+  bool is_ccs = false;
 };
 
 // dtls_clear_outgoing_messages releases all buffered outgoing messages.
@@ -1036,8 +1040,57 @@
 
 // Transport buffers.
 
-// ssl_read_buffer returns the current read buffer.
-Span<uint8_t> ssl_read_buffer(SSL *ssl);
+class SSLBuffer {
+ public:
+  SSLBuffer() {}
+  ~SSLBuffer() { Clear(); }
+
+  SSLBuffer(const SSLBuffer &) = delete;
+  SSLBuffer &operator=(const SSLBuffer &) = delete;
+
+  uint8_t *data() { return buf_ + offset_; }
+  size_t size() const { return size_; }
+  bool empty() const { return size_ == 0; }
+  size_t cap() const { return cap_; }
+
+  Span<uint8_t> span() { return MakeSpan(data(), size()); }
+
+  Span<uint8_t> remaining() {
+    return MakeSpan(data() + size(), cap() - size());
+  }
+
+  // Clear releases the buffer.
+  void Clear();
+
+  // EnsureCap ensures the buffer has capacity at least |new_cap|, aligned such
+  // that data written after |header_len| is aligned to a
+  // |SSL3_ALIGN_PAYLOAD|-byte boundary. It returns true on success and false
+  // on error.
+  bool EnsureCap(size_t header_len, size_t new_cap);
+
+  // DidWrite extends the buffer by |len|. The caller must have filled in to
+  // this point.
+  void DidWrite(size_t len);
+
+  // Consume consumes |len| bytes from the front of the buffer.  The memory
+  // consumed will remain valid until the next call to |DiscardConsumed| or
+  // |Clear|.
+  void Consume(size_t len);
+
+  // DiscardConsumed discards the consumed bytes from the buffer. If the buffer
+  // is now empty, it releases memory used by it.
+  void DiscardConsumed();
+
+ private:
+  // buf_ is the memory allocated for this buffer.
+  uint8_t *buf_ = nullptr;
+  // offset_ is the offset into |buf_| which the buffer contents start at.
+  uint16_t offset_ = 0;
+  // size_ is the size of the buffer contents from |buf_| + |offset_|.
+  uint16_t size_ = 0;
+  // cap_ is how much memory beyond |buf_| + |offset_| is available.
+  uint16_t cap_ = 0;
+};
 
 // ssl_read_buffer_extend_to extends the read buffer to the desired length. For
 // TLS, it reads to the end of the buffer until the buffer is |len| bytes
@@ -1048,49 +1101,18 @@
 // non-empty.
 int ssl_read_buffer_extend_to(SSL *ssl, size_t len);
 
-// ssl_read_buffer_consume consumes |len| bytes from the read buffer. It
-// advances the data pointer and decrements the length. The memory consumed will
-// remain valid until the next call to |ssl_read_buffer_extend| or it is
-// discarded with |ssl_read_buffer_discard|.
-void ssl_read_buffer_consume(SSL *ssl, size_t len);
-
-// ssl_read_buffer_discard discards the consumed bytes from the read buffer. If
-// the buffer is now empty, it releases memory used by it.
-void ssl_read_buffer_discard(SSL *ssl);
-
-// ssl_read_buffer_clear releases all memory associated with the read buffer and
-// zero-initializes it.
-void ssl_read_buffer_clear(SSL *ssl);
-
-// ssl_handle_open_record handles the result of passing |ssl_read_buffer| to a
-// record-processing function. If |ret| is a success or if the caller should
-// retry, it returns one and sets |*out_retry|. Otherwise, it returns <= 0.
+// ssl_handle_open_record handles the result of passing |ssl->s3->read_buffer|
+// to a record-processing function. If |ret| is a success or if the caller
+// should retry, it returns one and sets |*out_retry|. Otherwise, it returns <=
+// 0.
 int ssl_handle_open_record(SSL *ssl, bool *out_retry, ssl_open_record_t ret,
                            size_t consumed, uint8_t alert);
 
-// ssl_write_buffer_is_pending returns one if the write buffer has pending data
-// and zero if is empty.
-int ssl_write_buffer_is_pending(const SSL *ssl);
-
-// ssl_write_buffer_init initializes the write buffer. On success, it sets
-// |*out_ptr| to the start of the write buffer with space for up to |max_len|
-// bytes. It returns one on success and zero on failure. Call
-// |ssl_write_buffer_set_len| to complete initialization.
-int ssl_write_buffer_init(SSL *ssl, uint8_t **out_ptr, size_t max_len);
-
-// ssl_write_buffer_set_len is called after |ssl_write_buffer_init| to complete
-// initialization after |len| bytes are written to the buffer.
-void ssl_write_buffer_set_len(SSL *ssl, size_t len);
-
 // ssl_write_buffer_flush flushes the write buffer to the transport. It returns
 // one on success and <= 0 on error. For DTLS, whether or not the write
 // succeeds, the write buffer will be cleared.
 int ssl_write_buffer_flush(SSL *ssl);
 
-// ssl_write_buffer_clear releases all memory associated with the write buffer
-// and zero-initializes it.
-void ssl_write_buffer_clear(SSL *ssl);
-
 
 // Certificate functions.
 
@@ -1468,10 +1490,7 @@
   uint16_t early_data_written = 0;
 };
 
-SSL_HANDSHAKE *ssl_handshake_new(SSL *ssl);
-
-// ssl_handshake_free releases all memory associated with |hs|.
-void ssl_handshake_free(SSL_HANDSHAKE *hs);
+UniquePtr<SSL_HANDSHAKE> ssl_handshake_new(SSL *ssl);
 
 // ssl_check_message_type checks if |msg| has type |type|. If so it returns
 // one. Otherwise, it sends an alert and returns zero.
@@ -1556,6 +1575,10 @@
     SSL_HANDSHAKE *hs, Array<uint8_t> *out,
     enum ssl_cert_verify_context_t cert_verify_context);
 
+// ssl_is_alpn_protocol_allowed returns whether |protocol| is a valid server
+// selection for |ssl|'s client preferences.
+bool ssl_is_alpn_protocol_allowed(const SSL *ssl, Span<const uint8_t> protocol);
+
 // ssl_negotiate_alpn negotiates the ALPN extension, if applicable. It returns
 // true on successful negotiation or if nothing was negotiated. It returns false
 // and sets |*out_alert| to an alert on error.
@@ -2134,17 +2157,6 @@
   bool ed25519_enabled:1;
 };
 
-struct SSL3_BUFFER {
-  // buf is the memory allocated for this buffer.
-  uint8_t *buf;
-  // offset is the offset into |buf| which the buffer contents start at.
-  uint16_t offset;
-  // len is the length of the buffer contents from |buf| + |offset|.
-  uint16_t len;
-  // cap is how much memory beyond |buf| + |offset| is available.
-  uint16_t cap;
-};
-
 // An ssl_shutdown_t describes the shutdown state of one end of the connection,
 // whether it is alive or has been shutdown via close_notify or fatal alert.
 enum ssl_shutdown_t {
@@ -2154,55 +2166,65 @@
 };
 
 struct SSL3_STATE {
-  uint8_t read_sequence[8];
-  uint8_t write_sequence[8];
+  static constexpr bool kAllowUniquePtr = true;
 
-  uint8_t server_random[SSL3_RANDOM_SIZE];
-  uint8_t client_random[SSL3_RANDOM_SIZE];
+  SSL3_STATE();
+  ~SSL3_STATE();
+
+  uint8_t read_sequence[8] = {0};
+  uint8_t write_sequence[8] = {0};
+
+  uint8_t server_random[SSL3_RANDOM_SIZE] = {0};
+  uint8_t client_random[SSL3_RANDOM_SIZE] = {0};
 
   // read_buffer holds data from the transport to be processed.
-  SSL3_BUFFER read_buffer;
+  SSLBuffer read_buffer;
   // write_buffer holds data to be written to the transport.
-  SSL3_BUFFER write_buffer;
+  SSLBuffer write_buffer;
 
   // pending_app_data is the unconsumed application data. It points into
   // |read_buffer|.
   Span<uint8_t> pending_app_data;
 
   // partial write - check the numbers match
-  unsigned int wnum;  // number of bytes sent so far
-  int wpend_tot;      // number bytes written
-  int wpend_type;
-  int wpend_ret;  // number of bytes submitted
-  const uint8_t *wpend_buf;
+  unsigned int wnum = 0;  // number of bytes sent so far
+  int wpend_tot = 0;      // number bytes written
+  int wpend_type = 0;
+  int wpend_ret = 0;  // number of bytes submitted
+  const uint8_t *wpend_buf = nullptr;
 
   // read_shutdown is the shutdown state for the read half of the connection.
-  enum ssl_shutdown_t read_shutdown;
+  enum ssl_shutdown_t read_shutdown = ssl_shutdown_none;
 
   // write_shutdown is the shutdown state for the write half of the connection.
-  enum ssl_shutdown_t write_shutdown;
+  enum ssl_shutdown_t write_shutdown = ssl_shutdown_none;
 
   // read_error, if |read_shutdown| is |ssl_shutdown_error|, is the error for
   // the receive half of the connection.
-  ERR_SAVE_STATE *read_error;
+  UniquePtr<ERR_SAVE_STATE> read_error;
 
-  int alert_dispatch;
+  int alert_dispatch = 0;
 
-  int total_renegotiations;
+  int total_renegotiations = 0;
+
+  // This holds a variable that indicates what we were doing when a 0 or -1 is
+  // returned.  This is needed for non-blocking IO so we know what request
+  // needs re-doing when in SSL_accept or SSL_connect
+  int rwstate = SSL_NOTHING;
 
   // early_data_skipped is the amount of early data that has been skipped by the
   // record layer.
-  uint16_t early_data_skipped;
+  uint16_t early_data_skipped = 0;
 
   // empty_record_count is the number of consecutive empty records received.
-  uint8_t empty_record_count;
+  uint8_t empty_record_count = 0;
 
   // warning_alert_count is the number of consecutive warning alerts
   // received.
-  uint8_t warning_alert_count;
+  uint8_t warning_alert_count = 0;
 
   // key_update_count is the number of consecutive KeyUpdates received.
-  uint8_t key_update_count;
+  uint8_t key_update_count = 0;
 
   // skip_early_data instructs the record layer to skip unexpected early data
   // messages when 0RTT is rejected.
@@ -2246,56 +2268,49 @@
   // wpend_pending is true if we have a pending write outstanding.
   bool wpend_pending:1;
 
-  uint8_t send_alert[2];
+  uint8_t send_alert[2] = {0};
+
+  // hs_buf is the buffer of handshake data to process.
+  UniquePtr<BUF_MEM> hs_buf;
 
   // pending_flight is the pending outgoing flight. This is used to flush each
   // handshake flight in a single write. |write_buffer| must be written out
   // before this data.
-  BUF_MEM *pending_flight;
+  UniquePtr<BUF_MEM> pending_flight;
 
   // pending_flight_offset is the number of bytes of |pending_flight| which have
   // been successfully written.
-  uint32_t pending_flight_offset;
+  uint32_t pending_flight_offset = 0;
 
   // aead_read_ctx is the current read cipher state.
-  SSLAEADContext *aead_read_ctx;
+  UniquePtr<SSLAEADContext> aead_read_ctx;
 
   // aead_write_ctx is the current write cipher state.
-  SSLAEADContext *aead_write_ctx;
+  UniquePtr<SSLAEADContext> aead_write_ctx;
 
   // hs is the handshake state for the current handshake or NULL if there isn't
   // one.
-  SSL_HANDSHAKE *hs;
+  UniquePtr<SSL_HANDSHAKE> hs;
 
-  uint8_t write_traffic_secret[EVP_MAX_MD_SIZE];
-  uint8_t read_traffic_secret[EVP_MAX_MD_SIZE];
-  uint8_t exporter_secret[EVP_MAX_MD_SIZE];
-  uint8_t early_exporter_secret[EVP_MAX_MD_SIZE];
-  uint8_t write_traffic_secret_len;
-  uint8_t read_traffic_secret_len;
-  uint8_t exporter_secret_len;
-  uint8_t early_exporter_secret_len;
+  uint8_t write_traffic_secret[EVP_MAX_MD_SIZE] = {0};
+  uint8_t read_traffic_secret[EVP_MAX_MD_SIZE] = {0};
+  uint8_t exporter_secret[EVP_MAX_MD_SIZE] = {0};
+  uint8_t early_exporter_secret[EVP_MAX_MD_SIZE] = {0};
+  uint8_t write_traffic_secret_len = 0;
+  uint8_t read_traffic_secret_len = 0;
+  uint8_t exporter_secret_len = 0;
+  uint8_t early_exporter_secret_len = 0;
 
   // Connection binding to prevent renegotiation attacks
-  uint8_t previous_client_finished[12];
-  uint8_t previous_client_finished_len;
-  uint8_t previous_server_finished_len;
-  uint8_t previous_server_finished[12];
-
-  // State pertaining to the pending handshake.
-  //
-  // TODO(davidben): Move everything not needed after the handshake completes to
-  // |hs| and remove this.
-  struct {
-    uint8_t new_mac_secret_len;
-    uint8_t new_key_len;
-    uint8_t new_fixed_iv_len;
-  } tmp;
+  uint8_t previous_client_finished[12] = {0};
+  uint8_t previous_client_finished_len = 0;
+  uint8_t previous_server_finished_len = 0;
+  uint8_t previous_server_finished[12] = {0};
 
   // established_session is the session established by the connection. This
   // session is only filled upon the completion of the handshake and is
   // immutable.
-  SSL_SESSION *established_session;
+  UniquePtr<SSL_SESSION> established_session;
 
   // Next protocol negotiation. For the client, this is the protocol that we
   // sent in NextProtocol and is set when handling ServerHello extensions.
@@ -2303,8 +2318,7 @@
   // For a server, this is the client's selected_protocol from NextProtocol and
   // is set when handling the NextProtocol message, before the Finished
   // message.
-  uint8_t *next_proto_negotiated;
-  size_t next_proto_negotiated_len;
+  Array<uint8_t> next_proto_negotiated;
 
   // ALPN information
   // (we are in the process of transitioning from NPN to ALPN.)
@@ -2312,22 +2326,21 @@
   // In a server these point to the selected ALPN protocol after the
   // ClientHello has been processed. In a client these contain the protocol
   // that the server selected once the ServerHello has been processed.
-  uint8_t *alpn_selected;
-  size_t alpn_selected_len;
+  Array<uint8_t> alpn_selected;
 
   // hostname, on the server, is the value of the SNI extension.
-  char *hostname;
+  UniquePtr<char> hostname;
 
   // For a server:
   //     If |tlsext_channel_id_valid| is true, then this contains the
   //     verified Channel ID from the client: a P256 point, (x,y), where
   //     each are big-endian values.
-  uint8_t tlsext_channel_id[64];
+  uint8_t tlsext_channel_id[64] = {0};
 
   // ticket_age_skew is the difference, in seconds, between the client-sent
   // ticket age and the server-computed value in TLS 1.3 server connections
   // which resumed a session.
-  int32_t ticket_age_skew;
+  int32_t ticket_age_skew = 0;
 };
 
 // lengths of messages
@@ -2351,18 +2364,26 @@
 
 // An hm_fragment is an incoming DTLS message, possibly not yet assembled.
 struct hm_fragment {
+  static constexpr bool kAllowUniquePtr = true;
+
+  hm_fragment() {}
+  hm_fragment(const hm_fragment &) = delete;
+  hm_fragment &operator=(const hm_fragment &) = delete;
+
+  ~hm_fragment();
+
   // type is the type of the message.
-  uint8_t type;
+  uint8_t type = 0;
   // seq is the sequence number of this message.
-  uint16_t seq;
+  uint16_t seq = 0;
   // msg_len is the length of the message body.
-  uint32_t msg_len;
+  uint32_t msg_len = 0;
   // data is a pointer to the message, including message header. It has length
   // |DTLS1_HM_HEADER_LENGTH| + |msg_len|.
-  uint8_t *data;
+  uint8_t *data = nullptr;
   // reassembly is a bitmask of |msg_len| bits corresponding to which parts of
   // the message have been received. It is NULL if the message is complete.
-  uint8_t *reassembly;
+  uint8_t *reassembly = nullptr;
 };
 
 struct OPENSSL_timeval {
@@ -2371,6 +2392,11 @@
 };
 
 struct DTLS1_STATE {
+  static constexpr bool kAllowUniquePtr = true;
+
+  DTLS1_STATE();
+  ~DTLS1_STATE();
+
   // has_change_cipher_spec is true if we have received a ChangeCipherSpec from
   // the peer in this epoch.
   bool has_change_cipher_spec:1;
@@ -2385,54 +2411,54 @@
   // peer sent the final flight.
   bool flight_has_reply:1;
 
-  uint8_t cookie[DTLS1_COOKIE_LENGTH];
-  size_t cookie_len;
+  uint8_t cookie[DTLS1_COOKIE_LENGTH] = {0};
+  size_t cookie_len = 0;
 
   // The current data and handshake epoch.  This is initially undefined, and
   // starts at zero once the initial handshake is completed.
-  uint16_t r_epoch;
-  uint16_t w_epoch;
+  uint16_t r_epoch = 0;
+  uint16_t w_epoch = 0;
 
   // records being received in the current epoch
   DTLS1_BITMAP bitmap;
 
-  uint16_t handshake_write_seq;
-  uint16_t handshake_read_seq;
+  uint16_t handshake_write_seq = 0;
+  uint16_t handshake_read_seq = 0;
 
   // save last sequence number for retransmissions
-  uint8_t last_write_sequence[8];
-  SSLAEADContext *last_aead_write_ctx;
+  uint8_t last_write_sequence[8] = {0};
+  UniquePtr<SSLAEADContext> last_aead_write_ctx;
 
   // incoming_messages is a ring buffer of incoming handshake messages that have
   // yet to be processed. The front of the ring buffer is message number
   // |handshake_read_seq|, at position |handshake_read_seq| %
   // |SSL_MAX_HANDSHAKE_FLIGHT|.
-  hm_fragment *incoming_messages[SSL_MAX_HANDSHAKE_FLIGHT];
+  UniquePtr<hm_fragment> incoming_messages[SSL_MAX_HANDSHAKE_FLIGHT];
 
   // outgoing_messages is the queue of outgoing messages from the last handshake
   // flight.
   DTLS_OUTGOING_MESSAGE outgoing_messages[SSL_MAX_HANDSHAKE_FLIGHT];
-  uint8_t outgoing_messages_len;
+  uint8_t outgoing_messages_len = 0;
 
   // outgoing_written is the number of outgoing messages that have been
   // written.
-  uint8_t outgoing_written;
+  uint8_t outgoing_written = 0;
   // outgoing_offset is the number of bytes of the next outgoing message have
   // been written.
-  uint32_t outgoing_offset;
+  uint32_t outgoing_offset = 0;
 
-  unsigned int mtu;  // max DTLS packet size
+  unsigned mtu = 0;  // max DTLS packet size
 
   // num_timeouts is the number of times the retransmit timer has fired since
   // the last time it was reset.
-  unsigned int num_timeouts;
+  unsigned num_timeouts = 0;
 
   // Indicates when the last handshake msg or heartbeat sent will
   // timeout.
-  struct OPENSSL_timeval next_timeout;
+  struct OPENSSL_timeval next_timeout = {0, 0};
 
   // timeout_duration_ms is the timeout duration in milliseconds.
-  unsigned timeout_duration_ms;
+  unsigned timeout_duration_ms = 0;
 };
 
 // SSLConnection backs the public |SSL| type. Due to compatibility constraints,
@@ -2472,8 +2498,6 @@
   // progress.
   enum ssl_hs_wait_t (*do_handshake)(SSL_HANDSHAKE *hs);
 
-  BUF_MEM *init_buf;  // buffer used during init
-
   SSL3_STATE *s3;   // SSLv3 variables
   DTLS1_STATE *d1;  // DTLSv1 variables
 
@@ -2493,11 +2517,6 @@
   // This is used to hold the server certificate used
   CERT *cert;
 
-  // This holds a variable that indicates what we were doing when a 0 or -1 is
-  // returned.  This is needed for non-blocking IO so we know what request
-  // needs re-doing when in SSL_accept or SSL_connect
-  int rwstate;
-
   // initial_timeout_duration_ms is the default DTLS timeout duration in
   // milliseconds. It's used to initialize the timer any time it's restarted.
   unsigned initial_timeout_duration_ms;
@@ -2601,7 +2620,6 @@
 };
 
 // From draft-ietf-tls-tls13-18, used in determining PSK modes.
-#define SSL_PSK_KE     0x0
 #define SSL_PSK_DHE_KE 0x1
 
 // From draft-ietf-tls-tls13-16, used in determining whether to respond with a
diff --git a/src/ssl/s3_both.cc b/src/ssl/s3_both.cc
index 1459085..ede4ba7 100644
--- a/src/ssl/s3_both.cc
+++ b/src/ssl/s3_both.cc
@@ -137,9 +137,9 @@
   // We'll never add a flight while in the process of writing it out.
   assert(ssl->s3->pending_flight_offset == 0);
 
-  if (ssl->s3->pending_flight == NULL) {
-    ssl->s3->pending_flight = BUF_MEM_new();
-    if (ssl->s3->pending_flight == NULL) {
+  if (ssl->s3->pending_flight == nullptr) {
+    ssl->s3->pending_flight.reset(BUF_MEM_new());
+    if (ssl->s3->pending_flight == nullptr) {
       return false;
     }
   }
@@ -152,7 +152,7 @@
   }
 
   size_t len;
-  if (!BUF_MEM_reserve(ssl->s3->pending_flight, new_cap) ||
+  if (!BUF_MEM_reserve(ssl->s3->pending_flight.get(), new_cap) ||
       !tls_seal_record(ssl,
                        (uint8_t *)ssl->s3->pending_flight->data +
                            ssl->s3->pending_flight->length,
@@ -229,7 +229,7 @@
 }
 
 int ssl3_flush_flight(SSL *ssl) {
-  if (ssl->s3->pending_flight == NULL) {
+  if (ssl->s3->pending_flight == nullptr) {
     return 1;
   }
 
@@ -246,10 +246,10 @@
 
   // If there is pending data in the write buffer, it must be flushed out before
   // any new data in pending_flight.
-  if (ssl_write_buffer_is_pending(ssl)) {
+  if (!ssl->s3->write_buffer.empty()) {
     int ret = ssl_write_buffer_flush(ssl);
     if (ret <= 0) {
-      ssl->rwstate = SSL_WRITING;
+      ssl->s3->rwstate = SSL_WRITING;
       return ret;
     }
   }
@@ -261,7 +261,7 @@
         ssl->s3->pending_flight->data + ssl->s3->pending_flight_offset,
         ssl->s3->pending_flight->length - ssl->s3->pending_flight_offset);
     if (ret <= 0) {
-      ssl->rwstate = SSL_WRITING;
+      ssl->s3->rwstate = SSL_WRITING;
       return ret;
     }
 
@@ -269,12 +269,11 @@
   }
 
   if (BIO_flush(ssl->wbio) <= 0) {
-    ssl->rwstate = SSL_WRITING;
+    ssl->s3->rwstate = SSL_WRITING;
     return -1;
   }
 
-  BUF_MEM_free(ssl->s3->pending_flight);
-  ssl->s3->pending_flight = NULL;
+  ssl->s3->pending_flight.reset();
   ssl->s3->pending_flight_offset = 0;
   return 1;
 }
@@ -303,7 +302,7 @@
     return ssl_open_record_partial;
   }
 
-  CBS v2_client_hello = CBS(ssl_read_buffer(ssl).subspan(2, msg_length));
+  CBS v2_client_hello = CBS(ssl->s3->read_buffer.span().subspan(2, msg_length));
   // The V2ClientHello without the length is incorporated into the handshake
   // hash. This is only ever called at the start of the handshake, so hs is
   // guaranteed to be non-NULL.
@@ -352,9 +351,9 @@
                                1 /* compression length */ + 1 /* compression */;
   ScopedCBB client_hello;
   CBB hello_body, cipher_suites;
-  if (!BUF_MEM_reserve(ssl->init_buf, max_v3_client_hello) ||
-      !CBB_init_fixed(client_hello.get(), (uint8_t *)ssl->init_buf->data,
-                      ssl->init_buf->max) ||
+  if (!BUF_MEM_reserve(ssl->s3->hs_buf.get(), max_v3_client_hello) ||
+      !CBB_init_fixed(client_hello.get(), (uint8_t *)ssl->s3->hs_buf->data,
+                      ssl->s3->hs_buf->max) ||
       !CBB_add_u8(client_hello.get(), SSL3_MT_CLIENT_HELLO) ||
       !CBB_add_u24_length_prefixed(client_hello.get(), &hello_body) ||
       !CBB_add_u16(&hello_body, version) ||
@@ -387,7 +386,7 @@
   // Add the null compression scheme and finish.
   if (!CBB_add_u8(&hello_body, 1) ||
       !CBB_add_u8(&hello_body, 0) ||
-      !CBB_finish(client_hello.get(), NULL, &ssl->init_buf->length)) {
+      !CBB_finish(client_hello.get(), NULL, &ssl->s3->hs_buf->length)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return ssl_open_record_error;
   }
@@ -399,15 +398,15 @@
 
 static bool parse_message(const SSL *ssl, SSLMessage *out,
                           size_t *out_bytes_needed) {
-  if (ssl->init_buf == NULL) {
+  if (!ssl->s3->hs_buf) {
     *out_bytes_needed = 4;
     return false;
   }
 
   CBS cbs;
   uint32_t len;
-  CBS_init(&cbs, reinterpret_cast<const uint8_t *>(ssl->init_buf->data),
-           ssl->init_buf->length);
+  CBS_init(&cbs, reinterpret_cast<const uint8_t *>(ssl->s3->hs_buf->data),
+           ssl->s3->hs_buf->length);
   if (!CBS_get_u8(&cbs, &out->type) ||
       !CBS_get_u24(&cbs, &len)) {
     *out_bytes_needed = 4;
@@ -419,7 +418,7 @@
     return false;
   }
 
-  CBS_init(&out->raw, reinterpret_cast<const uint8_t *>(ssl->init_buf->data),
+  CBS_init(&out->raw, reinterpret_cast<const uint8_t *>(ssl->s3->hs_buf->data),
            4 + len);
   out->is_v2_hello = ssl->s3->is_v2_hello;
   return true;
@@ -469,16 +468,16 @@
     }
   }
 
-  return ssl->init_buf != NULL && ssl->init_buf->length > msg_len;
+  return ssl->s3->hs_buf && ssl->s3->hs_buf->length > msg_len;
 }
 
 ssl_open_record_t ssl3_open_handshake(SSL *ssl, size_t *out_consumed,
                                       uint8_t *out_alert, Span<uint8_t> in) {
   *out_consumed = 0;
   // Re-create the handshake buffer if needed.
-  if (ssl->init_buf == NULL) {
-    ssl->init_buf = BUF_MEM_new();
-    if (ssl->init_buf == NULL) {
+  if (!ssl->s3->hs_buf) {
+    ssl->s3->hs_buf.reset(BUF_MEM_new());
+    if (!ssl->s3->hs_buf) {
       *out_alert = SSL_AD_INTERNAL_ERROR;
       return ssl_open_record_error;
     }
@@ -552,7 +551,7 @@
   }
 
   // Append the entire handshake record to the buffer.
-  if (!BUF_MEM_append(ssl->init_buf, body.data(), body.size())) {
+  if (!BUF_MEM_append(ssl->s3->hs_buf.get(), body.data(), body.size())) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
     return ssl_open_record_error;
   }
@@ -563,23 +562,23 @@
 void ssl3_next_message(SSL *ssl) {
   SSLMessage msg;
   if (!ssl3_get_message(ssl, &msg) ||
-      ssl->init_buf == NULL ||
-      ssl->init_buf->length < CBS_len(&msg.raw)) {
+      !ssl->s3->hs_buf ||
+      ssl->s3->hs_buf->length < CBS_len(&msg.raw)) {
     assert(0);
     return;
   }
 
-  OPENSSL_memmove(ssl->init_buf->data, ssl->init_buf->data + CBS_len(&msg.raw),
-                  ssl->init_buf->length - CBS_len(&msg.raw));
-  ssl->init_buf->length -= CBS_len(&msg.raw);
+  OPENSSL_memmove(ssl->s3->hs_buf->data,
+                  ssl->s3->hs_buf->data + CBS_len(&msg.raw),
+                  ssl->s3->hs_buf->length - CBS_len(&msg.raw));
+  ssl->s3->hs_buf->length -= CBS_len(&msg.raw);
   ssl->s3->is_v2_hello = false;
   ssl->s3->has_message = false;
 
   // Post-handshake messages are rare, so release the buffer after every
   // message. During the handshake, |on_handshake_complete| will release it.
-  if (!SSL_in_init(ssl) && ssl->init_buf->length == 0) {
-    BUF_MEM_free(ssl->init_buf);
-    ssl->init_buf = NULL;
+  if (!SSL_in_init(ssl) && ssl->s3->hs_buf->length == 0) {
+    ssl->s3->hs_buf.reset();
   }
 }
 
diff --git a/src/ssl/s3_lib.cc b/src/ssl/s3_lib.cc
index f3f99fa..b925cd7 100644
--- a/src/ssl/s3_lib.cc
+++ b/src/ssl/s3_lib.cc
@@ -164,30 +164,35 @@
 
 namespace bssl {
 
+SSL3_STATE::SSL3_STATE()
+    : skip_early_data(false),
+      have_version(false),
+      v2_hello_done(false),
+      is_v2_hello(false),
+      has_message(false),
+      initial_handshake_complete(false),
+      session_reused(false),
+      send_connection_binding(false),
+      tlsext_channel_id_valid(false),
+      key_update_pending(false),
+      wpend_pending(false) {}
+
+SSL3_STATE::~SSL3_STATE() {}
+
 bool ssl3_new(SSL *ssl) {
-  UniquePtr<SSLAEADContext> aead_read_ctx =
-      SSLAEADContext::CreateNullCipher(SSL_is_dtls(ssl));
-  UniquePtr<SSLAEADContext> aead_write_ctx =
-      SSLAEADContext::CreateNullCipher(SSL_is_dtls(ssl));
-  if (!aead_read_ctx || !aead_write_ctx) {
+  UniquePtr<SSL3_STATE> s3 = MakeUnique<SSL3_STATE>();
+  if (!s3) {
     return false;
   }
 
-  SSL3_STATE *s3 = (SSL3_STATE *)OPENSSL_malloc(sizeof *s3);
-  if (s3 == NULL) {
-    return false;
-  }
-  OPENSSL_memset(s3, 0, sizeof *s3);
-
+  s3->aead_read_ctx = SSLAEADContext::CreateNullCipher(SSL_is_dtls(ssl));
+  s3->aead_write_ctx = SSLAEADContext::CreateNullCipher(SSL_is_dtls(ssl));
   s3->hs = ssl_handshake_new(ssl);
-  if (s3->hs == NULL) {
-    OPENSSL_free(s3);
+  if (!s3->aead_read_ctx || !s3->aead_write_ctx || !s3->hs) {
     return false;
   }
 
-  s3->aead_read_ctx = aead_read_ctx.release();
-  s3->aead_write_ctx = aead_write_ctx.release();
-  ssl->s3 = s3;
+  ssl->s3 = s3.release();
 
   // Set the version to the highest supported version.
   //
@@ -203,20 +208,7 @@
     return;
   }
 
-  ssl_read_buffer_clear(ssl);
-  ssl_write_buffer_clear(ssl);
-
-  ERR_SAVE_STATE_free(ssl->s3->read_error);
-  SSL_SESSION_free(ssl->s3->established_session);
-  ssl_handshake_free(ssl->s3->hs);
-  OPENSSL_free(ssl->s3->next_proto_negotiated);
-  OPENSSL_free(ssl->s3->alpn_selected);
-  OPENSSL_free(ssl->s3->hostname);
-  Delete(ssl->s3->aead_read_ctx);
-  Delete(ssl->s3->aead_write_ctx);
-  BUF_MEM_free(ssl->s3->pending_flight);
-
-  OPENSSL_free(ssl->s3);
+  Delete(ssl->s3);
   ssl->s3 = NULL;
 }
 
diff --git a/src/ssl/s3_pkt.cc b/src/ssl/s3_pkt.cc
index 7966e6f..285abb3 100644
--- a/src/ssl/s3_pkt.cc
+++ b/src/ssl/s3_pkt.cc
@@ -124,9 +124,9 @@
 
 namespace bssl {
 
-static int do_ssl3_write(SSL *ssl, int type, const uint8_t *buf, unsigned len);
+static int do_ssl3_write(SSL *ssl, int type, const uint8_t *in, unsigned len);
 
-int ssl3_write_app_data(SSL *ssl, bool *out_needs_handshake, const uint8_t *buf,
+int ssl3_write_app_data(SSL *ssl, bool *out_needs_handshake, const uint8_t *in,
                         int len) {
   assert(ssl_can_write(ssl));
   assert(!ssl->s3->aead_write_ctx->is_null_cipher());
@@ -180,7 +180,7 @@
       nw = n;
     }
 
-    int ret = do_ssl3_write(ssl, SSL3_RT_APPLICATION_DATA, &buf[tot], nw);
+    int ret = do_ssl3_write(ssl, SSL3_RT_APPLICATION_DATA, &in[tot], nw);
     if (ret <= 0) {
       ssl->s3->wnum = tot;
       return ret;
@@ -199,11 +199,11 @@
   }
 }
 
-static int ssl3_write_pending(SSL *ssl, int type, const uint8_t *buf,
+static int ssl3_write_pending(SSL *ssl, int type, const uint8_t *in,
                               unsigned int len) {
   if (ssl->s3->wpend_tot > (int)len ||
       (!(ssl->mode & SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER) &&
-       ssl->s3->wpend_buf != buf) ||
+       ssl->s3->wpend_buf != in) ||
       ssl->s3->wpend_type != type) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_WRITE_RETRY);
     return -1;
@@ -218,13 +218,14 @@
 }
 
 // do_ssl3_write writes an SSL record of the given type.
-static int do_ssl3_write(SSL *ssl, int type, const uint8_t *buf, unsigned len) {
+static int do_ssl3_write(SSL *ssl, int type, const uint8_t *in, unsigned len) {
   // If there is still data from the previous record, flush it.
   if (ssl->s3->wpend_pending) {
-    return ssl3_write_pending(ssl, type, buf, len);
+    return ssl3_write_pending(ssl, type, in, len);
   }
 
-  if (len > SSL3_RT_MAX_PLAIN_LENGTH) {
+  SSLBuffer *buf = &ssl->s3->write_buffer;
+  if (len > SSL3_RT_MAX_PLAIN_LENGTH || buf->size() > 0) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return -1;
   }
@@ -234,7 +235,7 @@
   }
 
   size_t flight_len = 0;
-  if (ssl->s3->pending_flight != NULL) {
+  if (ssl->s3->pending_flight != nullptr) {
     flight_len =
         ssl->s3->pending_flight->length - ssl->s3->pending_flight_offset;
   }
@@ -246,9 +247,7 @@
   }
   max_out += flight_len;
 
-  uint8_t *out;
-  size_t ciphertext_len;
-  if (!ssl_write_buffer_init(ssl, &out, max_out)) {
+  if (!buf->EnsureCap(flight_len + ssl_seal_align_prefix_len(ssl), max_out)) {
     return -1;
   }
 
@@ -256,20 +255,22 @@
   // acknowledgment or 0-RTT key change messages. |pending_flight| must be clear
   // when data is added to |write_buffer| or it will be written in the wrong
   // order.
-  if (ssl->s3->pending_flight != NULL) {
+  if (ssl->s3->pending_flight != nullptr) {
     OPENSSL_memcpy(
-        out, ssl->s3->pending_flight->data + ssl->s3->pending_flight_offset,
+        buf->remaining().data(),
+        ssl->s3->pending_flight->data + ssl->s3->pending_flight_offset,
         flight_len);
-    BUF_MEM_free(ssl->s3->pending_flight);
-    ssl->s3->pending_flight = NULL;
+    ssl->s3->pending_flight.reset();
     ssl->s3->pending_flight_offset = 0;
+    buf->DidWrite(flight_len);
   }
 
-  if (!tls_seal_record(ssl, out + flight_len, &ciphertext_len,
-                       max_out - flight_len, type, buf, len)) {
+  size_t ciphertext_len;
+  if (!tls_seal_record(ssl, buf->remaining().data(), &ciphertext_len,
+                       buf->remaining().size(), type, in, len)) {
     return -1;
   }
-  ssl_write_buffer_set_len(ssl, flight_len + ciphertext_len);
+  buf->DidWrite(ciphertext_len);
 
   // Now that we've made progress on the connection, uncork KeyUpdate
   // acknowledgments.
@@ -278,13 +279,13 @@
   // Memorize arguments so that ssl3_write_pending can detect bad write retries
   // later.
   ssl->s3->wpend_tot = len;
-  ssl->s3->wpend_buf = buf;
+  ssl->s3->wpend_buf = in;
   ssl->s3->wpend_type = type;
   ssl->s3->wpend_ret = len;
   ssl->s3->wpend_pending = true;
 
   // We now just need to write the buffer.
-  return ssl3_write_pending(ssl, type, buf, len);
+  return ssl3_write_pending(ssl, type, in, len);
 }
 
 ssl_open_record_t ssl3_open_app_data(SSL *ssl, Span<uint8_t> *out,
@@ -320,11 +321,11 @@
       return ssl_open_record_error;
     }
 
-    if (ssl->init_buf == NULL) {
-      ssl->init_buf = BUF_MEM_new();
+    if (!ssl->s3->hs_buf) {
+      ssl->s3->hs_buf.reset(BUF_MEM_new());
     }
-    if (ssl->init_buf == NULL ||
-        !BUF_MEM_append(ssl->init_buf, body.data(), body.size())) {
+    if (!ssl->s3->hs_buf ||
+        !BUF_MEM_append(ssl->s3->hs_buf.get(), body.data(), body.size())) {
       *out_alert = SSL_AD_INTERNAL_ERROR;
       return ssl_open_record_error;
     }
@@ -408,7 +409,7 @@
   ssl->s3->alert_dispatch = 1;
   ssl->s3->send_alert[0] = level;
   ssl->s3->send_alert[1] = desc;
-  if (!ssl_write_buffer_is_pending(ssl)) {
+  if (ssl->s3->write_buffer.empty()) {
     // Nothing is being written out, so the alert may be dispatched
     // immediately.
     return ssl->method->dispatch_alert(ssl);
diff --git a/src/ssl/ssl_buffer.cc b/src/ssl/ssl_buffer.cc
index 412df73..da1de93 100644
--- a/src/ssl/ssl_buffer.cc
+++ b/src/ssl/ssl_buffer.cc
@@ -36,17 +36,22 @@
 static_assert((SSL3_ALIGN_PAYLOAD & (SSL3_ALIGN_PAYLOAD - 1)) == 0,
               "SSL3_ALIGN_PAYLOAD must be a power of 2");
 
-// ensure_buffer ensures |buf| has capacity at least |cap|, aligned such that
-// data written after |header_len| is aligned to a |SSL3_ALIGN_PAYLOAD|-byte
-// boundary. It returns one on success and zero on error.
-static int ensure_buffer(SSL3_BUFFER *buf, size_t header_len, size_t cap) {
-  if (cap > 0xffff) {
+void SSLBuffer::Clear() {
+  free(buf_);  // Allocated with malloc().
+  buf_ = nullptr;
+  offset_ = 0;
+  size_ = 0;
+  cap_ = 0;
+}
+
+bool SSLBuffer::EnsureCap(size_t header_len, size_t new_cap) {
+  if (new_cap > 0xffff) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
+    return false;
   }
 
-  if (buf->cap >= cap) {
-    return 1;
+  if (cap_ >= new_cap) {
+    return true;
   }
 
   // Add up to |SSL3_ALIGN_PAYLOAD| - 1 bytes of slack for alignment.
@@ -54,88 +59,88 @@
   // Since this buffer gets allocated quite frequently and doesn't contain any
   // sensitive data, we allocate with malloc rather than |OPENSSL_malloc| and
   // avoid zeroing on free.
-  uint8_t *new_buf = (uint8_t *)malloc(cap + SSL3_ALIGN_PAYLOAD - 1);
+  uint8_t *new_buf = (uint8_t *)malloc(new_cap + SSL3_ALIGN_PAYLOAD - 1);
   if (new_buf == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-    return 0;
+    return false;
   }
 
   // Offset the buffer such that the record body is aligned.
   size_t new_offset =
       (0 - header_len - (uintptr_t)new_buf) & (SSL3_ALIGN_PAYLOAD - 1);
 
-  if (buf->buf != NULL) {
-    OPENSSL_memcpy(new_buf + new_offset, buf->buf + buf->offset, buf->len);
-    free(buf->buf);  // Allocated with malloc().
+  if (buf_ != NULL) {
+    OPENSSL_memcpy(new_buf + new_offset, buf_ + offset_, size_);
+    free(buf_);  // Allocated with malloc().
   }
 
-  buf->buf = new_buf;
-  buf->offset = new_offset;
-  buf->cap = cap;
-  return 1;
+  buf_ = new_buf;
+  offset_ = new_offset;
+  cap_ = new_cap;
+  return true;
 }
 
-static void consume_buffer(SSL3_BUFFER *buf, size_t len) {
-  if (len > buf->len) {
+void SSLBuffer::DidWrite(size_t new_size) {
+  if (new_size > cap() - size()) {
     abort();
   }
-  buf->offset += (uint16_t)len;
-  buf->len -= (uint16_t)len;
-  buf->cap -= (uint16_t)len;
+  size_ += new_size;
 }
 
-static void clear_buffer(SSL3_BUFFER *buf) {
-  free(buf->buf);  // Allocated with malloc().
-  OPENSSL_memset(buf, 0, sizeof(SSL3_BUFFER));
+void SSLBuffer::Consume(size_t len) {
+  if (len > size_) {
+    abort();
+  }
+  offset_ += (uint16_t)len;
+  size_ -= (uint16_t)len;
+  cap_ -= (uint16_t)len;
 }
 
-Span<uint8_t> ssl_read_buffer(SSL *ssl) {
-  return MakeSpan(ssl->s3->read_buffer.buf + ssl->s3->read_buffer.offset,
-                  ssl->s3->read_buffer.len);
+void SSLBuffer::DiscardConsumed() {
+  if (size_ == 0) {
+    Clear();
+  }
 }
 
 static int dtls_read_buffer_next_packet(SSL *ssl) {
-  SSL3_BUFFER *buf = &ssl->s3->read_buffer;
+  SSLBuffer *buf = &ssl->s3->read_buffer;
 
-  if (buf->len > 0) {
+  if (!buf->empty()) {
     // It is an error to call |dtls_read_buffer_extend| when the read buffer is
     // not empty.
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return -1;
   }
 
-  // Read a single packet from |ssl->rbio|. |buf->cap| must fit in an int.
-  int ret = BIO_read(ssl->rbio, buf->buf + buf->offset, (int)buf->cap);
+  // Read a single packet from |ssl->rbio|. |buf->cap()| must fit in an int.
+  int ret = BIO_read(ssl->rbio, buf->data(), static_cast<int>(buf->cap()));
   if (ret <= 0) {
-    ssl->rwstate = SSL_READING;
+    ssl->s3->rwstate = SSL_READING;
     return ret;
   }
-  // |BIO_read| was bound by |buf->cap|, so this cannot overflow.
-  buf->len = (uint16_t)ret;
+  buf->DidWrite(static_cast<size_t>(ret));
   return 1;
 }
 
 static int tls_read_buffer_extend_to(SSL *ssl, size_t len) {
-  SSL3_BUFFER *buf = &ssl->s3->read_buffer;
+  SSLBuffer *buf = &ssl->s3->read_buffer;
 
-  if (len > buf->cap) {
+  if (len > buf->cap()) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL);
     return -1;
   }
 
   // Read until the target length is reached.
-  while (buf->len < len) {
+  while (buf->size() < len) {
     // The amount of data to read is bounded by |buf->cap|, which must fit in an
     // int.
-    int ret = BIO_read(ssl->rbio, buf->buf + buf->offset + buf->len,
-                       (int)(len - buf->len));
+    int ret = BIO_read(ssl->rbio, buf->data() + buf->size(),
+                       static_cast<int>(len - buf->size()));
     if (ret <= 0) {
-      ssl->rwstate = SSL_READING;
+      ssl->s3->rwstate = SSL_READING;
       return ret;
     }
-    // |BIO_read| was bound by |buf->cap - buf->len|, so this cannot
-    // overflow.
-    buf->len += (uint16_t)ret;
+    buf->DidWrite(static_cast<size_t>(ret));
   }
 
   return 1;
@@ -143,7 +148,7 @@
 
 int ssl_read_buffer_extend_to(SSL *ssl, size_t len) {
   // |ssl_read_buffer_extend_to| implicitly discards any consumed data.
-  ssl_read_buffer_discard(ssl);
+  ssl->s3->read_buffer.DiscardConsumed();
 
   if (SSL_is_dtls(ssl)) {
     static_assert(
@@ -154,7 +159,7 @@
     len = DTLS1_RT_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH;
   }
 
-  if (!ensure_buffer(&ssl->s3->read_buffer, ssl_record_prefix_len(ssl), len)) {
+  if (!ssl->s3->read_buffer.EnsureCap(ssl_record_prefix_len(ssl), len)) {
     return -1;
   }
 
@@ -174,43 +179,20 @@
   if (ret <= 0) {
     // If the buffer was empty originally and remained empty after attempting to
     // extend it, release the buffer until the next attempt.
-    ssl_read_buffer_discard(ssl);
+    ssl->s3->read_buffer.DiscardConsumed();
   }
   return ret;
 }
 
-void ssl_read_buffer_consume(SSL *ssl, size_t len) {
-  SSL3_BUFFER *buf = &ssl->s3->read_buffer;
-
-  consume_buffer(buf, len);
-
-  // The TLS stack never reads beyond the current record, so there will never be
-  // unconsumed data. If read-ahead is ever reimplemented,
-  // |ssl_read_buffer_discard| will require a |memcpy| to shift the excess back
-  // to the front of the buffer, to ensure there is enough space for the next
-  // record.
-  assert(SSL_is_dtls(ssl) || len == 0 || buf->len == 0);
-}
-
-void ssl_read_buffer_discard(SSL *ssl) {
-  if (ssl->s3->read_buffer.len == 0) {
-    ssl_read_buffer_clear(ssl);
-  }
-}
-
-void ssl_read_buffer_clear(SSL *ssl) {
-  clear_buffer(&ssl->s3->read_buffer);
-}
-
 int ssl_handle_open_record(SSL *ssl, bool *out_retry, ssl_open_record_t ret,
                            size_t consumed, uint8_t alert) {
   *out_retry = false;
   if (ret != ssl_open_record_partial) {
-    ssl_read_buffer_consume(ssl, consumed);
+    ssl->s3->read_buffer.Consume(consumed);
   }
   if (ret != ssl_open_record_success) {
     // Nothing was returned to the caller, so discard anything marked consumed.
-    ssl_read_buffer_discard(ssl);
+    ssl->s3->read_buffer.DiscardConsumed();
   }
   switch (ret) {
     case ssl_open_record_success:
@@ -243,10 +225,6 @@
 }
 
 
-int ssl_write_buffer_is_pending(const SSL *ssl) {
-  return ssl->s3->write_buffer.len > 0;
-}
-
 static_assert(SSL3_RT_HEADER_LENGTH * 2 +
                       SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD * 2 +
                       SSL3_RT_MAX_PLAIN_LENGTH <=
@@ -258,61 +236,37 @@
                   0xffff,
               "maximum DTLS write buffer is too large");
 
-int ssl_write_buffer_init(SSL *ssl, uint8_t **out_ptr, size_t max_len) {
-  SSL3_BUFFER *buf = &ssl->s3->write_buffer;
-
-  if (buf->buf != NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
-  }
-
-  if (!ensure_buffer(buf, ssl_seal_align_prefix_len(ssl), max_len)) {
-    return 0;
-  }
-  *out_ptr = buf->buf + buf->offset;
-  return 1;
-}
-
-void ssl_write_buffer_set_len(SSL *ssl, size_t len) {
-  SSL3_BUFFER *buf = &ssl->s3->write_buffer;
-
-  if (len > buf->cap) {
-    abort();
-  }
-  buf->len = len;
-}
-
 static int tls_write_buffer_flush(SSL *ssl) {
-  SSL3_BUFFER *buf = &ssl->s3->write_buffer;
+  SSLBuffer *buf = &ssl->s3->write_buffer;
 
-  while (buf->len > 0) {
-    int ret = BIO_write(ssl->wbio, buf->buf + buf->offset, buf->len);
+  while (!buf->empty()) {
+    int ret = BIO_write(ssl->wbio, buf->data(), buf->size());
     if (ret <= 0) {
-      ssl->rwstate = SSL_WRITING;
+      ssl->s3->rwstate = SSL_WRITING;
       return ret;
     }
-    consume_buffer(buf, (size_t)ret);
+    buf->Consume(static_cast<size_t>(ret));
   }
-  ssl_write_buffer_clear(ssl);
+  buf->Clear();
   return 1;
 }
 
 static int dtls_write_buffer_flush(SSL *ssl) {
-  SSL3_BUFFER *buf = &ssl->s3->write_buffer;
-  if (buf->len == 0) {
+  SSLBuffer *buf = &ssl->s3->write_buffer;
+  if (buf->empty()) {
     return 1;
   }
 
-  int ret = BIO_write(ssl->wbio, buf->buf + buf->offset, buf->len);
+  int ret = BIO_write(ssl->wbio, buf->data(), buf->size());
   if (ret <= 0) {
-    ssl->rwstate = SSL_WRITING;
+    ssl->s3->rwstate = SSL_WRITING;
     // If the write failed, drop the write buffer anyway. Datagram transports
     // can't write half a packet, so the caller is expected to retry from the
     // top.
-    ssl_write_buffer_clear(ssl);
+    buf->Clear();
     return ret;
   }
-  ssl_write_buffer_clear(ssl);
+  buf->Clear();
   return 1;
 }
 
@@ -329,8 +283,4 @@
   }
 }
 
-void ssl_write_buffer_clear(SSL *ssl) {
-  clear_buffer(&ssl->s3->write_buffer);
-}
-
 }  // namespace bssl
diff --git a/src/ssl/ssl_lib.cc b/src/ssl/ssl_lib.cc
index e3f8a88..2fc6ffd 100644
--- a/src/ssl/ssl_lib.cc
+++ b/src/ssl/ssl_lib.cc
@@ -201,20 +201,19 @@
 void ssl_reset_error_state(SSL *ssl) {
   // Functions which use |SSL_get_error| must reset I/O and error state on
   // entry.
-  ssl->rwstate = SSL_NOTHING;
+  ssl->s3->rwstate = SSL_NOTHING;
   ERR_clear_error();
   ERR_clear_system_error();
 }
 
 void ssl_set_read_error(SSL* ssl) {
   ssl->s3->read_shutdown = ssl_shutdown_error;
-  ERR_SAVE_STATE_free(ssl->s3->read_error);
-  ssl->s3->read_error = ERR_save_state();
+  ssl->s3->read_error.reset(ERR_save_state());
 }
 
 static bool check_read_error(const SSL *ssl) {
   if (ssl->s3->read_shutdown == ssl_shutdown_error) {
-    ERR_restore_state(ssl->s3->read_error);
+    ERR_restore_state(ssl->s3->read_error.get());
     return false;
   }
   return true;
@@ -300,16 +299,16 @@
   // A client may see new sessions on abbreviated handshakes if the server
   // decides to renew the ticket. Once the handshake is completed, it should be
   // inserted into the cache.
-  if (ssl->s3->established_session != ssl->session ||
+  if (ssl->s3->established_session.get() != ssl->session ||
       (!ssl->server && hs->ticket_expected)) {
     if (use_internal_cache) {
-      SSL_CTX_add_session(ctx, ssl->s3->established_session);
+      SSL_CTX_add_session(ctx, ssl->s3->established_session.get());
     }
     if (ctx->new_session_cb != NULL) {
-      SSL_SESSION_up_ref(ssl->s3->established_session);
-      if (!ctx->new_session_cb(ssl, ssl->s3->established_session)) {
+      SSL_SESSION_up_ref(ssl->s3->established_session.get());
+      if (!ctx->new_session_cb(ssl, ssl->s3->established_session.get())) {
         // |new_session_cb|'s return value signals whether it took ownership.
-        SSL_SESSION_free(ssl->s3->established_session);
+        SSL_SESSION_free(ssl->s3->established_session.get());
       }
     }
   }
@@ -717,8 +716,6 @@
     goto err;
   }
 
-  ssl->rwstate = SSL_NOTHING;
-
   CRYPTO_new_ex_data(&ssl->ex_data);
 
   ssl->psk_identity_hint = NULL;
@@ -763,8 +760,6 @@
   BIO_free_all(ssl->rbio);
   BIO_free_all(ssl->wbio);
 
-  BUF_MEM_free(ssl->init_buf);
-
   // add extra stuff
   ssl_cipher_preference_list_free(ssl->cipher_list);
 
@@ -860,7 +855,7 @@
   }
 
   // Run the handshake.
-  SSL_HANDSHAKE *hs = ssl->s3->hs;
+  SSL_HANDSHAKE *hs = ssl->s3->hs.get();
 
   bool early_return = false;
   int ret = ssl_run_handshake(hs, &early_return);
@@ -872,8 +867,7 @@
 
   // Destroy the handshake object if the handshake has completely finished.
   if (!early_return) {
-    ssl_handshake_free(ssl->s3->hs);
-    ssl->s3->hs = NULL;
+    ssl->s3->hs.reset();
   }
 
   return 1;
@@ -937,18 +931,18 @@
   // protocol, namely in HTTPS, just before reading the HTTP response. Require
   // the record-layer be idle and avoid complexities of sending a handshake
   // record while an application_data record is being written.
-  if (ssl_write_buffer_is_pending(ssl) ||
+  if (!ssl->s3->write_buffer.empty() ||
       ssl->s3->write_shutdown != ssl_shutdown_none) {
     goto no_renegotiation;
   }
 
   // Begin a new handshake.
-  if (ssl->s3->hs != NULL) {
+  if (ssl->s3->hs != nullptr) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return 0;
   }
   ssl->s3->hs = ssl_handshake_new(ssl);
-  if (ssl->s3->hs == NULL) {
+  if (ssl->s3->hs == nullptr) {
     return 0;
   }
 
@@ -1004,7 +998,7 @@
     uint8_t alert = SSL_AD_DECODE_ERROR;
     size_t consumed = 0;
     auto ret = ssl_open_app_data(ssl, &ssl->s3->pending_app_data, &consumed,
-                                 &alert, ssl_read_buffer(ssl));
+                                 &alert, ssl->s3->read_buffer.span());
     bool retry;
     int bio_ret = ssl_handle_open_record(ssl, &retry, ret, consumed, alert);
     if (bio_ret <= 0) {
@@ -1029,7 +1023,7 @@
   ssl->s3->pending_app_data =
       ssl->s3->pending_app_data.subspan(static_cast<size_t>(ret));
   if (ssl->s3->pending_app_data.empty()) {
-    ssl_read_buffer_discard(ssl);
+    ssl->s3->read_buffer.DiscardConsumed();
   }
   return ret;
 }
@@ -1125,7 +1119,7 @@
       // time out because the peer never received our close_notify. Report to
       // the caller that the channel has fully shut down.
       if (ssl->s3->read_shutdown == ssl_shutdown_error) {
-        ERR_restore_state(ssl->s3->read_error);
+        ERR_restore_state(ssl->s3->read_error.get());
         return -1;
       }
       ssl->s3->read_shutdown = ssl_shutdown_close_notify;
@@ -1190,7 +1184,7 @@
 }
 
 void SSL_reset_early_data_reject(SSL *ssl) {
-  SSL_HANDSHAKE *hs = ssl->s3->hs;
+  SSL_HANDSHAKE *hs = ssl->s3->hs.get();
   if (hs == NULL ||
       hs->wait != ssl_hs_early_data_rejected) {
     abort();
@@ -1242,7 +1236,7 @@
     return SSL_ERROR_SYSCALL;
   }
 
-  switch (ssl->rwstate) {
+  switch (ssl->s3->rwstate) {
     case SSL_PENDING_SESSION:
       return SSL_ERROR_PENDING_SESSION;
 
@@ -1863,7 +1857,7 @@
     return ssl->tlsext_hostname;
   }
 
-  return ssl->s3->hostname;
+  return ssl->s3->hostname.get();
 }
 
 int SSL_get_servername_type(const SSL *ssl) {
@@ -1996,8 +1990,8 @@
 
 void SSL_get0_next_proto_negotiated(const SSL *ssl, const uint8_t **out_data,
                                     unsigned *out_len) {
-  *out_data = ssl->s3->next_proto_negotiated;
-  *out_len = ssl->s3->next_proto_negotiated_len;
+  *out_data = ssl->s3->next_proto_negotiated.data();
+  *out_len = ssl->s3->next_proto_negotiated.size();
 }
 
 void SSL_CTX_set_next_protos_advertised_cb(
@@ -2054,8 +2048,8 @@
     *out_data = ssl->s3->hs->early_session->early_alpn;
     *out_len = ssl->s3->hs->early_session->early_alpn_len;
   } else {
-    *out_data = ssl->s3->alpn_selected;
-    *out_len = ssl->s3->alpn_selected_len;
+    *out_data = ssl->s3->alpn_selected.data();
+    *out_len = ssl->s3->alpn_selected.size();
   }
 }
 
@@ -2296,7 +2290,7 @@
   return CRYPTO_get_ex_data(&ctx->ex_data, idx);
 }
 
-int SSL_want(const SSL *ssl) { return ssl->rwstate; }
+int SSL_want(const SSL *ssl) { return ssl->s3->rwstate; }
 
 void SSL_CTX_set_tmp_rsa_callback(SSL_CTX *ctx,
                                   RSA *(*cb)(SSL *ssl, int is_export,
@@ -2434,7 +2428,7 @@
   // This returns false once all the handshake state has been finalized, to
   // allow callbacks and getters based on SSL_in_init to return the correct
   // values.
-  SSL_HANDSHAKE *hs = ssl->s3->hs;
+  SSL_HANDSHAKE *hs = ssl->s3->hs.get();
   return hs != nullptr && !hs->handshake_finalized;
 }
 
@@ -2547,7 +2541,7 @@
 }
 
 const SSL_CIPHER *SSL_get_pending_cipher(const SSL *ssl) {
-  SSL_HANDSHAKE *hs = ssl->s3->hs;
+  SSL_HANDSHAKE *hs = ssl->s3->hs.get();
   if (hs == NULL) {
     return NULL;
   }
@@ -2574,23 +2568,12 @@
   // In OpenSSL, reusing a client |SSL| with |SSL_clear| causes the previously
   // established session to be offered the next time around. wpa_supplicant
   // depends on this behavior, so emulate it.
-  SSL_SESSION *session = NULL;
+  UniquePtr<SSL_SESSION> session;
   if (!ssl->server && ssl->s3->established_session != NULL) {
-    session = ssl->s3->established_session;
-    SSL_SESSION_up_ref(session);
+    session.reset(ssl->s3->established_session.get());
+    SSL_SESSION_up_ref(session.get());
   }
 
-  // TODO(davidben): Some state on |ssl| is reset both in |SSL_new| and
-  // |SSL_clear| because it is per-connection state rather than configuration
-  // state. Per-connection state should be on |ssl->s3| and |ssl->d1| so it is
-  // naturally reset at the right points between |SSL_new|, |SSL_clear|, and
-  // |ssl3_new|.
-
-  ssl->rwstate = SSL_NOTHING;
-
-  BUF_MEM_free(ssl->init_buf);
-  ssl->init_buf = NULL;
-
   // The ssl->d1->mtu is simultaneously configuration (preserved across
   // clear) and connection-specific state (gets reset).
   //
@@ -2602,7 +2585,6 @@
 
   ssl->method->ssl_free(ssl);
   if (!ssl->method->ssl_new(ssl)) {
-    SSL_SESSION_free(session);
     return 0;
   }
 
@@ -2610,9 +2592,8 @@
     ssl->d1->mtu = mtu;
   }
 
-  if (session != NULL) {
-    SSL_set_session(ssl, session);
-    SSL_SESSION_free(session);
+  if (session != nullptr) {
+    SSL_set_session(ssl, session.get());
   }
 
   return 1;
diff --git a/src/ssl/ssl_session.cc b/src/ssl/ssl_session.cc
index ea3c53f..34e7b31 100644
--- a/src/ssl/ssl_session.cc
+++ b/src/ssl/ssl_session.cc
@@ -999,9 +999,9 @@
   // we return the intermediate session, either |session| (for resumption) or
   // |new_session| if doing a full handshake.
   if (!SSL_in_init(ssl)) {
-    return ssl->s3->established_session;
+    return ssl->s3->established_session.get();
   }
-  SSL_HANDSHAKE *hs = ssl->s3->hs;
+  SSL_HANDSHAKE *hs = ssl->s3->hs.get();
   if (hs->early_session) {
     return hs->early_session.get();
   }
diff --git a/src/ssl/ssl_stat.cc b/src/ssl/ssl_stat.cc
index 37e0324..01153e9 100644
--- a/src/ssl/ssl_stat.cc
+++ b/src/ssl/ssl_stat.cc
@@ -89,12 +89,12 @@
 
 
 const char *SSL_state_string_long(const SSL *ssl) {
-  if (ssl->s3->hs == NULL) {
+  if (ssl->s3->hs == nullptr) {
     return "SSL negotiation finished successfully";
   }
 
-  return ssl->server ? ssl_server_handshake_state(ssl->s3->hs)
-                     : ssl_client_handshake_state(ssl->s3->hs);
+  return ssl->server ? ssl_server_handshake_state(ssl->s3->hs.get())
+                     : ssl_client_handshake_state(ssl->s3->hs.get());
 }
 
 const char *SSL_state_string(const SSL *ssl) {
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
index 5e9cde1..ab50a57 100644
--- a/src/ssl/ssl_test.cc
+++ b/src/ssl/ssl_test.cc
@@ -75,10 +75,7 @@
     {TLS1_VERSION, VersionParam::is_tls, "TLS1"},
     {TLS1_1_VERSION, VersionParam::is_tls, "TLS1_1"},
     {TLS1_2_VERSION, VersionParam::is_tls, "TLS1_2"},
-// TLS 1.3 requires RSA-PSS, which is disabled for Android system builds.
-#if !defined(BORINGSSL_ANDROID_SYSTEM)
     {TLS1_3_VERSION, VersionParam::is_tls, "TLS1_3"},
-#endif
     {DTLS1_VERSION, VersionParam::is_dtls, "DTLS1"},
     {DTLS1_2_VERSION, VersionParam::is_dtls, "DTLS1_2"},
 };
@@ -1974,8 +1971,6 @@
       0x01, 0x00, 0x00, 0x1f, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x17, 0x00,
       0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00,
       0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18}},
-  // This test assumes RSA-PSS, which is disabled for Android system builds.
-#if !defined(BORINGSSL_ANDROID_SYSTEM)
     {TLS1_2_VERSION,
      {0x16, 0x03, 0x01, 0x00, 0x8e, 0x01, 0x00, 0x00, 0x8a, 0x03, 0x03, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -1990,7 +1985,6 @@
       0x05, 0x05, 0x01, 0x08, 0x06, 0x06, 0x01, 0x02, 0x01, 0x00, 0x0b, 0x00,
       0x02, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00,
       0x17, 0x00, 0x18}},
-#endif
     // TODO(davidben): Add a change detector for TLS 1.3 once the spec and our
     // implementation has settled enough that it won't change.
   };
diff --git a/src/ssl/t1_enc.cc b/src/ssl/t1_enc.cc
index e1578bd..1298a10 100644
--- a/src/ssl/t1_enc.cc
+++ b/src/ssl/t1_enc.cc
@@ -316,68 +316,65 @@
   return 1;
 }
 
-static int tls1_setup_key_block(SSL_HANDSHAKE *hs) {
-  SSL *const ssl = hs->ssl;
-  if (!hs->key_block.empty()) {
-    return 1;
-  }
-
-  SSL_SESSION *session = ssl->session;
-  if (hs->new_session) {
-    session = hs->new_session.get();
-  }
-
+static bool get_key_block_lengths(const SSL *ssl, size_t *out_mac_secret_len,
+                                  size_t *out_key_len, size_t *out_iv_len,
+                                  const SSL_CIPHER *cipher) {
   const EVP_AEAD *aead = NULL;
-  size_t mac_secret_len, fixed_iv_len;
-  if (session->cipher == NULL ||
-      !ssl_cipher_get_evp_aead(&aead, &mac_secret_len, &fixed_iv_len,
-                               session->cipher, ssl_protocol_version(ssl),
-                               SSL_is_dtls(ssl))) {
+  if (!ssl_cipher_get_evp_aead(&aead, out_mac_secret_len, out_iv_len, cipher,
+                               ssl_protocol_version(ssl), SSL_is_dtls(ssl))) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CIPHER_OR_HASH_UNAVAILABLE);
-    return 0;
+    return false;
   }
-  size_t key_len = EVP_AEAD_key_length(aead);
-  if (mac_secret_len > 0) {
+
+  *out_key_len = EVP_AEAD_key_length(aead);
+  if (*out_mac_secret_len > 0) {
     // For "stateful" AEADs (i.e. compatibility with pre-AEAD cipher suites) the
     // key length reported by |EVP_AEAD_key_length| will include the MAC key
     // bytes and initial implicit IV.
-    if (key_len < mac_secret_len + fixed_iv_len) {
+    if (*out_key_len < *out_mac_secret_len + *out_iv_len) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      return 0;
+      return false;
     }
-    key_len -= mac_secret_len + fixed_iv_len;
+    *out_key_len -= *out_mac_secret_len + *out_iv_len;
   }
 
-  assert(mac_secret_len < 256);
-  assert(key_len < 256);
-  assert(fixed_iv_len < 256);
+  return true;
+}
 
-  ssl->s3->tmp.new_mac_secret_len = (uint8_t)mac_secret_len;
-  ssl->s3->tmp.new_key_len = (uint8_t)key_len;
-  ssl->s3->tmp.new_fixed_iv_len = (uint8_t)fixed_iv_len;
+static bool setup_key_block(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
+  if (!hs->key_block.empty()) {
+    return true;
+  }
 
+  size_t mac_secret_len, key_len, fixed_iv_len;
   Array<uint8_t> key_block;
-  if (!key_block.Init(SSL_get_key_block_len(ssl)) ||
+  if (!get_key_block_lengths(ssl, &mac_secret_len, &key_len, &fixed_iv_len,
+                             hs->new_cipher) ||
+      !key_block.Init(2 * (mac_secret_len + key_len + fixed_iv_len)) ||
       !SSL_generate_key_block(ssl, key_block.data(), key_block.size())) {
-    return 0;
+    return false;
   }
 
   hs->key_block = std::move(key_block);
-  return 1;
+  return true;
 }
 
 int tls1_change_cipher_state(SSL_HANDSHAKE *hs,
                              evp_aead_direction_t direction) {
   SSL *const ssl = hs->ssl;
   // Ensure the key block is set up.
-  if (!tls1_setup_key_block(hs)) {
+  size_t mac_secret_len, key_len, iv_len;
+  if (!setup_key_block(hs) ||
+      !get_key_block_lengths(ssl, &mac_secret_len, &key_len, &iv_len,
+                             hs->new_cipher)) {
     return 0;
   }
 
-  size_t mac_secret_len = ssl->s3->tmp.new_mac_secret_len;
-  size_t key_len = ssl->s3->tmp.new_key_len;
-  size_t iv_len = ssl->s3->tmp.new_fixed_iv_len;
-  assert((mac_secret_len + key_len + iv_len) * 2 == hs->key_block.size());
+  if ((mac_secret_len + key_len + iv_len) * 2 != hs->key_block.size()) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return 0;
+  }
 
   Span<const uint8_t> key_block = hs->key_block;
   Span<const uint8_t> mac_secret, key, iv;
@@ -448,9 +445,14 @@
 using namespace bssl;
 
 size_t SSL_get_key_block_len(const SSL *ssl) {
-  return 2 * ((size_t)ssl->s3->tmp.new_mac_secret_len +
-              (size_t)ssl->s3->tmp.new_key_len +
-              (size_t)ssl->s3->tmp.new_fixed_iv_len);
+  size_t mac_secret_len, key_len, fixed_iv_len;
+  if (!get_key_block_lengths(ssl, &mac_secret_len, &key_len, &fixed_iv_len,
+                             SSL_get_current_cipher(ssl))) {
+    ERR_clear_error();
+    return 0;
+  }
+
+  return 2 * (mac_secret_len + key_len + fixed_iv_len);
 }
 
 int SSL_generate_key_block(const SSL *ssl, uint8_t *out, size_t out_len) {
diff --git a/src/ssl/t1_lib.cc b/src/ssl/t1_lib.cc
index 5d85cb0..31b51c9 100644
--- a/src/ssl/t1_lib.cc
+++ b/src/ssl/t1_lib.cc
@@ -418,25 +418,15 @@
     // List our preferred algorithms first.
     SSL_SIGN_ED25519,
     SSL_SIGN_ECDSA_SECP256R1_SHA256,
-#if !defined(BORINGSSL_ANDROID_SYSTEM)
     SSL_SIGN_RSA_PSS_SHA256,
-#endif
     SSL_SIGN_RSA_PKCS1_SHA256,
 
     // Larger hashes are acceptable.
     SSL_SIGN_ECDSA_SECP384R1_SHA384,
-#if !defined(BORINGSSL_ANDROID_SYSTEM)
     SSL_SIGN_RSA_PSS_SHA384,
-#endif
     SSL_SIGN_RSA_PKCS1_SHA384,
 
-    // TODO(davidben): Remove this.
-#if defined(BORINGSSL_ANDROID_SYSTEM)
-    SSL_SIGN_ECDSA_SECP521R1_SHA512,
-#endif
-#if !defined(BORINGSSL_ANDROID_SYSTEM)
     SSL_SIGN_RSA_PSS_SHA512,
-#endif
     SSL_SIGN_RSA_PKCS1_SHA512,
 
     // For now, SHA-1 is still accepted but least preferable.
@@ -454,24 +444,18 @@
     // List our preferred algorithms first.
     SSL_SIGN_ED25519,
     SSL_SIGN_ECDSA_SECP256R1_SHA256,
-#if !defined(BORINGSSL_ANDROID_SYSTEM)
     SSL_SIGN_RSA_PSS_SHA256,
-#endif
     SSL_SIGN_RSA_PKCS1_SHA256,
 
     // If needed, sign larger hashes.
     //
     // TODO(davidben): Determine which of these may be pruned.
     SSL_SIGN_ECDSA_SECP384R1_SHA384,
-#if !defined(BORINGSSL_ANDROID_SYSTEM)
     SSL_SIGN_RSA_PSS_SHA384,
-#endif
     SSL_SIGN_RSA_PKCS1_SHA384,
 
     SSL_SIGN_ECDSA_SECP521R1_SHA512,
-#if !defined(BORINGSSL_ANDROID_SYSTEM)
     SSL_SIGN_RSA_PSS_SHA512,
-#endif
     SSL_SIGN_RSA_PKCS1_SHA512,
 
     // If the peer supports nothing else, sign with SHA-1.
@@ -640,10 +624,12 @@
   }
 
   // Copy the hostname as a string.
-  if (!CBS_strdup(&host_name, &ssl->s3->hostname)) {
+  char *raw = nullptr;
+  if (!CBS_strdup(&host_name, &raw)) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
     return false;
   }
+  ssl->s3->hostname.reset(raw);
 
   hs->should_ack_sni = true;
   return true;
@@ -862,7 +848,7 @@
   }
 
   // Whether EMS is negotiated may not change on renegotiation.
-  if (ssl->s3->established_session != NULL &&
+  if (ssl->s3->established_session != nullptr &&
       hs->extended_master_secret !=
           !!ssl->s3->established_session->extended_master_secret) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_EMS_MISMATCH);
@@ -1150,7 +1136,7 @@
   assert(!SSL_is_dtls(ssl));
   assert(ssl->ctx->next_proto_select_cb != NULL);
 
-  if (ssl->s3->alpn_selected != NULL) {
+  if (!ssl->s3->alpn_selected.empty()) {
     // NPN and ALPN may not be negotiated in the same connection.
     *out_alert = SSL_AD_ILLEGAL_PARAMETER;
     OPENSSL_PUT_ERROR(SSL, SSL_R_NEGOTIATED_BOTH_NPN_AND_ALPN);
@@ -1172,22 +1158,14 @@
   uint8_t selected_len;
   if (ssl->ctx->next_proto_select_cb(
           ssl, &selected, &selected_len, orig_contents, orig_len,
-          ssl->ctx->next_proto_select_cb_arg) != SSL_TLSEXT_ERR_OK) {
+          ssl->ctx->next_proto_select_cb_arg) != SSL_TLSEXT_ERR_OK ||
+      !ssl->s3->next_proto_negotiated.CopyFrom(
+          MakeConstSpan(selected, selected_len))) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
     return false;
   }
 
-  OPENSSL_free(ssl->s3->next_proto_negotiated);
-  ssl->s3->next_proto_negotiated =
-      (uint8_t *)BUF_memdup(selected, selected_len);
-  if (ssl->s3->next_proto_negotiated == NULL) {
-    *out_alert = SSL_AD_INTERNAL_ERROR;
-    return false;
-  }
-
-  ssl->s3->next_proto_negotiated_len = selected_len;
   hs->next_proto_neg_seen = true;
-
   return true;
 }
 
@@ -1388,37 +1366,13 @@
     return false;
   }
 
-  if (!ssl->ctx->allow_unknown_alpn_protos) {
-    // Check that the protocol name is one of the ones we advertised.
-    bool protocol_ok = false;
-    CBS client_protocol_name_list, client_protocol_name;
-    CBS_init(&client_protocol_name_list, ssl->alpn_client_proto_list,
-             ssl->alpn_client_proto_list_len);
-    while (CBS_len(&client_protocol_name_list) > 0) {
-      if (!CBS_get_u8_length_prefixed(&client_protocol_name_list,
-                                      &client_protocol_name)) {
-        *out_alert = SSL_AD_INTERNAL_ERROR;
-        return false;
-      }
-
-      if (CBS_len(&client_protocol_name) == CBS_len(&protocol_name) &&
-          OPENSSL_memcmp(CBS_data(&client_protocol_name),
-                         CBS_data(&protocol_name),
-                         CBS_len(&protocol_name)) == 0) {
-        protocol_ok = true;
-        break;
-      }
-    }
-
-    if (!protocol_ok) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_ALPN_PROTOCOL);
-      *out_alert = SSL_AD_ILLEGAL_PARAMETER;
-      return false;
-    }
+  if (!ssl_is_alpn_protocol_allowed(ssl, protocol_name)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_ALPN_PROTOCOL);
+    *out_alert = SSL_AD_ILLEGAL_PARAMETER;
+    return false;
   }
 
-  if (!CBS_stow(&protocol_name, &ssl->s3->alpn_selected,
-                &ssl->s3->alpn_selected_len)) {
+  if (!ssl->s3->alpn_selected.CopyFrom(protocol_name)) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
     return false;
   }
@@ -1426,6 +1380,34 @@
   return true;
 }
 
+bool ssl_is_alpn_protocol_allowed(const SSL *ssl,
+                                  Span<const uint8_t> protocol) {
+  if (ssl->alpn_client_proto_list == nullptr) {
+    return false;
+  }
+
+  if (ssl->ctx->allow_unknown_alpn_protos) {
+    return true;
+  }
+
+  // Check that the protocol name is one of the ones we advertised.
+  CBS client_protocol_name_list, client_protocol_name;
+  CBS_init(&client_protocol_name_list, ssl->alpn_client_proto_list,
+           ssl->alpn_client_proto_list_len);
+  while (CBS_len(&client_protocol_name_list) > 0) {
+    if (!CBS_get_u8_length_prefixed(&client_protocol_name_list,
+                                    &client_protocol_name)) {
+      return false;
+    }
+
+    if (client_protocol_name == protocol) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
 bool ssl_negotiate_alpn(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                         const SSL_CLIENT_HELLO *client_hello) {
   SSL *const ssl = hs->ssl;
@@ -1470,13 +1452,11 @@
           ssl, &selected, &selected_len, CBS_data(&protocol_name_list),
           CBS_len(&protocol_name_list),
           ssl->ctx->alpn_select_cb_arg) == SSL_TLSEXT_ERR_OK) {
-    OPENSSL_free(ssl->s3->alpn_selected);
-    ssl->s3->alpn_selected = (uint8_t *)BUF_memdup(selected, selected_len);
-    if (ssl->s3->alpn_selected == NULL) {
+    if (!ssl->s3->alpn_selected.CopyFrom(
+            MakeConstSpan(selected, selected_len))) {
       *out_alert = SSL_AD_INTERNAL_ERROR;
       return false;
     }
-    ssl->s3->alpn_selected_len = selected_len;
   }
 
   return true;
@@ -1484,7 +1464,7 @@
 
 static bool ext_alpn_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
-  if (ssl->s3->alpn_selected == NULL) {
+  if (ssl->s3->alpn_selected.empty()) {
     return true;
   }
 
@@ -1493,8 +1473,8 @@
       !CBB_add_u16_length_prefixed(out, &contents) ||
       !CBB_add_u16_length_prefixed(&contents, &proto_list) ||
       !CBB_add_u8_length_prefixed(&proto_list, &proto) ||
-      !CBB_add_bytes(&proto, ssl->s3->alpn_selected,
-                     ssl->s3->alpn_selected_len) ||
+      !CBB_add_bytes(&proto, ssl->s3->alpn_selected.data(),
+                     ssl->s3->alpn_selected.size()) ||
       !CBB_flush(out)) {
     return false;
   }
@@ -1998,11 +1978,19 @@
 
 static bool ext_early_data_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
-  if (ssl->session == NULL ||
+  if (!ssl->cert->enable_early_data ||
+      // Session must be 0-RTT capable.
+      ssl->session == NULL ||
       ssl_session_protocol_version(ssl->session) < TLS1_3_VERSION ||
       ssl->session->ticket_max_early_data == 0 ||
+      // The second ClientHello never offers early data.
       hs->received_hello_retry_request ||
-      !ssl->cert->enable_early_data) {
+      // In case ALPN preferences changed since this session was established,
+      // avoid reporting a confusing value in |SSL_get0_alpn_selected|.
+      (ssl->session->early_alpn_len != 0 &&
+       !ssl_is_alpn_protocol_allowed(
+           ssl, MakeConstSpan(ssl->session->early_alpn,
+                              ssl->session->early_alpn_len)))) {
     return true;
   }
 
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index 1d7b996..d439cb5 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -1101,7 +1101,6 @@
 // Connect returns a new socket connected to localhost on |port| or -1 on
 // error.
 static int Connect(uint16_t port) {
-  time_t start_time = time(nullptr);
   for (int af : { AF_INET6, AF_INET }) {
     int sock = socket(af, SOCK_STREAM, 0);
     if (sock == -1) {
@@ -1148,11 +1147,6 @@
   }
 
   PrintSocketError("connect");
-  // TODO(davidben): Remove this logging when https://crbug.com/boringssl/199 is
-  // resolved.
-  fprintf(stderr, "start_time = %lld, end_time = %lld\n",
-          static_cast<long long>(start_time),
-          static_cast<long long>(time(nullptr)));
   return -1;
 }
 
@@ -2201,6 +2195,12 @@
       return false;
     }
 
+    if (is_resume && !is_retry && !config->is_server &&
+        config->expect_no_offer_early_data && SSL_in_early_data(ssl)) {
+      fprintf(stderr, "Client unexpectedly offered early data.\n");
+      return false;
+    }
+
     if (config->handshake_twice) {
       do {
         ret = SSL_do_handshake(ssl);
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index 0edbe5c..71b52f2 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -244,14 +244,6 @@
 	hc.incEpoch()
 }
 
-func (hc *halfConn) doKeyUpdate(c *Conn, isOutgoing bool) {
-	side := serverWrite
-	if c.isClient == isOutgoing {
-		side = clientWrite
-	}
-	hc.useTrafficSecret(hc.wireVersion, c.cipherSuite, updateTrafficSecret(c.cipherSuite.hash(), hc.trafficSecret), side)
-}
-
 // incSeq increments the sequence number.
 func (hc *halfConn) incSeq(isOutgoing bool) {
 	limit := 0
@@ -737,6 +729,26 @@
 	return b, bb
 }
 
+func (c *Conn) useInTrafficSecret(version uint16, suite *cipherSuite, secret []byte) error {
+	if c.hand.Len() != 0 {
+		return c.in.setErrorLocked(errors.New("tls: buffered handshake messages on cipher change"))
+	}
+	side := serverWrite
+	if !c.isClient {
+		side = clientWrite
+	}
+	c.in.useTrafficSecret(version, suite, secret, side)
+	return nil
+}
+
+func (c *Conn) useOutTrafficSecret(version uint16, suite *cipherSuite, secret []byte) {
+	side := serverWrite
+	if c.isClient {
+		side = clientWrite
+	}
+	c.out.useTrafficSecret(version, suite, secret, side)
+}
+
 func (c *Conn) doReadRecord(want recordType) (recordType, *block, error) {
 RestartReadRecord:
 	if c.isDTLS {
@@ -940,8 +952,11 @@
 			break
 		}
 		if !isResumptionExperiment(c.wireVersion) {
-			err := c.in.changeCipherSpec(c.config)
-			if err != nil {
+			if c.hand.Len() != 0 {
+				c.in.setErrorLocked(errors.New("tls: buffered handshake messages on cipher change"))
+				break
+			}
+			if err := c.in.changeCipherSpec(c.config); err != nil {
 				c.in.setErrorLocked(c.sendAlert(err.(alert)))
 			}
 		}
@@ -1522,7 +1537,9 @@
 		if c.config.Bugs.RejectUnsolicitedKeyUpdate {
 			return errors.New("tls: unexpected KeyUpdate message")
 		}
-		c.in.doKeyUpdate(c, false)
+		if err := c.useInTrafficSecret(c.in.wireVersion, c.cipherSuite, updateTrafficSecret(c.cipherSuite.hash(), c.in.trafficSecret)); err != nil {
+			return err
+		}
 		if keyUpdate.keyUpdateRequest == keyUpdateRequested {
 			c.keyUpdateRequested = true
 		}
@@ -1554,8 +1571,7 @@
 		return errors.New("tls: received invalid KeyUpdate message")
 	}
 
-	c.in.doKeyUpdate(c, false)
-	return nil
+	return c.useInTrafficSecret(c.in.wireVersion, c.cipherSuite, updateTrafficSecret(c.cipherSuite.hash(), c.in.trafficSecret))
 }
 
 func (c *Conn) Renegotiate() error {
@@ -1885,7 +1901,7 @@
 	if err := c.flushHandshake(); err != nil {
 		return err
 	}
-	c.out.doKeyUpdate(c, true)
+	c.useOutTrafficSecret(c.out.wireVersion, c.cipherSuite, updateTrafficSecret(c.cipherSuite.hash(), c.out.trafficSecret))
 	return nil
 }
 
diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go
index ef6a464..30105a5 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -411,7 +411,7 @@
 		finishedHash.addEntropy(session.masterSecret)
 		finishedHash.Write(helloBytes)
 		earlyTrafficSecret := finishedHash.deriveSecret(earlyTrafficLabel)
-		c.out.useTrafficSecret(session.wireVersion, pskCipherSuite, earlyTrafficSecret, clientWrite)
+		c.useOutTrafficSecret(session.wireVersion, pskCipherSuite, earlyTrafficSecret)
 		for _, earlyData := range c.config.Bugs.SendEarlyData {
 			if _, err := c.writeRecord(recordTypeApplicationData, earlyData); err != nil {
 				return err
@@ -754,7 +754,9 @@
 	// traffic key.
 	clientHandshakeTrafficSecret := hs.finishedHash.deriveSecret(clientHandshakeTrafficLabel)
 	serverHandshakeTrafficSecret := hs.finishedHash.deriveSecret(serverHandshakeTrafficLabel)
-	c.in.useTrafficSecret(c.wireVersion, hs.suite, serverHandshakeTrafficSecret, serverWrite)
+	if err := c.useInTrafficSecret(c.wireVersion, hs.suite, serverHandshakeTrafficSecret); err != nil {
+		return err
+	}
 
 	msg, err := c.readHandshake()
 	if err != nil {
@@ -888,7 +890,9 @@
 
 	// Switch to application data keys on read. In particular, any alerts
 	// from the client certificate are read over these keys.
-	c.in.useTrafficSecret(c.wireVersion, hs.suite, serverTrafficSecret, serverWrite)
+	if err := c.useInTrafficSecret(c.wireVersion, hs.suite, serverTrafficSecret); err != nil {
+		return err
+	}
 
 	// If we're expecting 0.5-RTT messages from the server, read them
 	// now.
@@ -934,7 +938,7 @@
 		c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
 	}
 
-	c.out.useTrafficSecret(c.wireVersion, hs.suite, clientHandshakeTrafficSecret, clientWrite)
+	c.useOutTrafficSecret(c.wireVersion, hs.suite, clientHandshakeTrafficSecret)
 
 	if certReq != nil && !c.config.Bugs.SkipClientCertificate {
 		certMsg := &certificateMsg{
@@ -1020,7 +1024,7 @@
 	c.flushHandshake()
 
 	// Switch to application data keys.
-	c.out.useTrafficSecret(c.wireVersion, hs.suite, clientTrafficSecret, clientWrite)
+	c.useOutTrafficSecret(c.wireVersion, hs.suite, clientTrafficSecret)
 
 	c.resumptionSecret = hs.finishedHash.deriveSecret(resumptionLabel)
 	return nil
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index 0ffb72c..f50772e 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -648,8 +648,7 @@
 		// AcceptAnyBinder is set. See https://crbug.com/115.
 		if hs.sessionState != nil && !config.Bugs.AcceptAnySession {
 			binderToVerify := newClientHello.pskBinders[pskIndex]
-			err := verifyPSKBinder(newClientHello, hs.sessionState, binderToVerify, append(oldClientHelloBytes, helloRetryRequest.marshal()...))
-			if err != nil {
+			if err := verifyPSKBinder(newClientHello, hs.sessionState, binderToVerify, append(oldClientHelloBytes, helloRetryRequest.marshal()...)); err != nil {
 				return err
 			}
 		}
@@ -664,7 +663,9 @@
 		}
 		if encryptedExtensions.extensions.hasEarlyData {
 			earlyTrafficSecret := hs.finishedHash.deriveSecret(earlyTrafficLabel)
-			c.in.useTrafficSecret(c.wireVersion, hs.suite, earlyTrafficSecret, clientWrite)
+			if err := c.useInTrafficSecret(c.wireVersion, hs.suite, earlyTrafficSecret); err != nil {
+				return err
+			}
 
 			for _, expectedMsg := range config.Bugs.ExpectEarlyData {
 				if err := c.readRecord(recordTypeApplicationData); err != nil {
@@ -761,7 +762,7 @@
 
 	// Switch to handshake traffic keys.
 	serverHandshakeTrafficSecret := hs.finishedHash.deriveSecret(serverHandshakeTrafficLabel)
-	c.out.useTrafficSecret(c.wireVersion, hs.suite, serverHandshakeTrafficSecret, serverWrite)
+	c.useOutTrafficSecret(c.wireVersion, hs.suite, serverHandshakeTrafficSecret)
 	// Derive handshake traffic read key, but don't switch yet.
 	clientHandshakeTrafficSecret := hs.finishedHash.deriveSecret(clientHandshakeTrafficLabel)
 
@@ -902,7 +903,7 @@
 
 	// Switch to application data keys on write. In particular, any alerts
 	// from the client certificate are sent over these keys.
-	c.out.useTrafficSecret(c.wireVersion, hs.suite, serverTrafficSecret, serverWrite)
+	c.useOutTrafficSecret(c.wireVersion, hs.suite, serverTrafficSecret)
 
 	// Send 0.5-RTT messages.
 	for _, halfRTTMsg := range config.Bugs.SendHalfRTTData {
@@ -928,7 +929,9 @@
 	}
 
 	// Switch input stream to handshake traffic keys.
-	c.in.useTrafficSecret(c.wireVersion, hs.suite, clientHandshakeTrafficSecret, clientWrite)
+	if err := c.useInTrafficSecret(c.wireVersion, hs.suite, clientHandshakeTrafficSecret); err != nil {
+		return err
+	}
 
 	// If we requested a client certificate, then the client must send a
 	// certificate message, even if it's empty.
@@ -1032,7 +1035,9 @@
 	hs.writeClientHash(clientFinished.marshal())
 
 	// Switch to application data keys on read.
-	c.in.useTrafficSecret(c.wireVersion, hs.suite, clientTrafficSecret, clientWrite)
+	if err := c.useInTrafficSecret(c.wireVersion, hs.suite, clientTrafficSecret); err != nil {
+		return err
+	}
 
 	c.cipherSuite = hs.suite
 	c.resumptionSecret = hs.finishedHash.deriveSecret(resumptionLabel)
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index e666404..d34eaa6 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -901,28 +901,20 @@
 // exit first.
 func acceptOrWait(listener *net.TCPListener, waitChan chan error) (net.Conn, error) {
 	type connOrError struct {
-		conn               net.Conn
-		err                error
-		startTime, endTime time.Time
+		conn net.Conn
+		err  error
 	}
 	connChan := make(chan connOrError, 1)
 	go func() {
-		startTime := time.Now()
 		if !*useGDB {
 			listener.SetDeadline(time.Now().Add(*idleTimeout))
 		}
 		conn, err := listener.Accept()
-		endTime := time.Now()
-		connChan <- connOrError{conn, err, startTime, endTime}
+		connChan <- connOrError{conn, err}
 		close(connChan)
 	}()
 	select {
 	case result := <-connChan:
-		if result.err != nil {
-			// TODO(davidben): Remove this logging when
-			// https://crbug.com/boringssl/199 is resolved.
-			fmt.Fprintf(os.Stderr, "acceptOrWait failed, startTime=%v, endTime=%v\n", result.startTime, result.endTime)
-		}
 		return result.conn, result.err
 	case childErr := <-waitChan:
 		waitChan <- childErr
@@ -2761,11 +2753,12 @@
 			},
 		},
 		{
-			// Test the server so there is a large certificate as
-			// well as application data.
+			// Test the TLS 1.2 server so there is a large
+			// unencrypted certificate as well as application data.
 			testType: serverTest,
-			name:     "MaxSendFragment",
+			name:     "MaxSendFragment-TLS12",
 			config: Config{
+				MaxVersion: VersionTLS12,
 				Bugs: ProtocolBugs{
 					MaxReceivePlaintext: 512,
 				},
@@ -2777,11 +2770,50 @@
 			},
 		},
 		{
-			// Test the server so there is a large certificate as
-			// well as application data.
+			// Test the TLS 1.2 server so there is a large
+			// unencrypted certificate as well as application data.
 			testType: serverTest,
-			name:     "MaxSendFragment-TooLarge",
+			name:     "MaxSendFragment-TLS12-TooLarge",
 			config: Config{
+				MaxVersion: VersionTLS12,
+				Bugs: ProtocolBugs{
+					// Ensure that some of the records are
+					// 512.
+					MaxReceivePlaintext: 511,
+				},
+			},
+			messageLen: 1024,
+			flags: []string{
+				"-max-send-fragment", "512",
+				"-read-size", "1024",
+			},
+			shouldFail:         true,
+			expectedLocalError: "local error: record overflow",
+		},
+		{
+			// Test the TLS 1.3 server so there is a large encrypted
+			// certificate as well as application data.
+			testType: serverTest,
+			name:     "MaxSendFragment-TLS13",
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					MaxReceivePlaintext: 512,
+				},
+			},
+			messageLen: 1024,
+			flags: []string{
+				"-max-send-fragment", "512",
+				"-read-size", "1024",
+			},
+		},
+		{
+			// Test the TLS 1.3 server so there is a large encrypted
+			// certificate as well as application data.
+			testType: serverTest,
+			name:     "MaxSendFragment-TLS13-TooLarge",
+			config: Config{
+				MaxVersion: VersionTLS13,
 				Bugs: ProtocolBugs{
 					// Ensure that some of the records are
 					// 512.
@@ -11726,6 +11758,28 @@
 		expectedError: ":ALPN_MISMATCH_ON_EARLY_DATA:",
 	})
 
+	// Test that the client does not offer early data if it is incompatible
+	// with ALPN preferences.
+	testCases = append(testCases, testCase{
+		testType: clientTest,
+		name:     "TLS13-EarlyData-ALPNPreferenceChanged",
+		config: Config{
+			MaxVersion:       VersionTLS13,
+			MaxEarlyDataSize: 16384,
+			NextProtos:       []string{"foo", "bar"},
+		},
+		resumeSession: true,
+		flags: []string{
+			"-enable-early-data",
+			"-expect-early-data-info",
+			"-expect-no-offer-early-data",
+			"-on-initial-advertise-alpn", "\x03foo",
+			"-on-resume-advertise-alpn", "\x03bar",
+			"-on-initial-expect-alpn", "foo",
+			"-on-resume-expect-alpn", "bar",
+		},
+	})
+
 	// Test that the server correctly rejects 0-RTT when the previous
 	// session did not allow early data on resumption.
 	testCases = append(testCases, testCase{
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index 6df8d2a..3354851 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -121,6 +121,7 @@
   { "-expect-no-session-id", &TestConfig::expect_no_session_id },
   { "-expect-accept-early-data", &TestConfig::expect_accept_early_data },
   { "-expect-reject-early-data", &TestConfig::expect_reject_early_data },
+  { "-expect-no-offer-early-data", &TestConfig::expect_no_offer_early_data },
   { "-no-op-extra-handshake", &TestConfig::no_op_extra_handshake },
   { "-handshake-twice", &TestConfig::handshake_twice },
   { "-allow-unknown-alpn-protos", &TestConfig::allow_unknown_alpn_protos },
diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h
index 9af64bc..b96d9e5 100644
--- a/src/ssl/test/test_config.h
+++ b/src/ssl/test/test_config.h
@@ -90,6 +90,7 @@
   bool expect_early_data_info = false;
   bool expect_accept_early_data = false;
   bool expect_reject_early_data = false;
+  bool expect_no_offer_early_data = false;
   bool use_ticket_callback = false;
   bool renew_ticket = false;
   bool enable_early_data = false;
diff --git a/src/ssl/tls13_client.cc b/src/ssl/tls13_client.cc
index a03c581..b6307ee 100644
--- a/src/ssl/tls13_client.cc
+++ b/src/ssl/tls13_client.cc
@@ -390,21 +390,21 @@
   }
 
   // Store the negotiated ALPN in the session.
-  if (ssl->s3->alpn_selected != NULL) {
+  if (!ssl->s3->alpn_selected.empty()) {
     hs->new_session->early_alpn = (uint8_t *)BUF_memdup(
-        ssl->s3->alpn_selected, ssl->s3->alpn_selected_len);
+        ssl->s3->alpn_selected.data(), ssl->s3->alpn_selected.size());
     if (hs->new_session->early_alpn == NULL) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       return ssl_hs_error;
     }
-    hs->new_session->early_alpn_len = ssl->s3->alpn_selected_len;
+    hs->new_session->early_alpn_len = ssl->s3->alpn_selected.size();
   }
 
   if (ssl->early_data_accepted) {
     if (hs->early_session->cipher != hs->new_session->cipher ||
-        hs->early_session->early_alpn_len != ssl->s3->alpn_selected_len ||
-        OPENSSL_memcmp(hs->early_session->early_alpn, ssl->s3->alpn_selected,
-                       ssl->s3->alpn_selected_len) != 0) {
+        MakeConstSpan(hs->early_session->early_alpn,
+                      hs->early_session->early_alpn_len) !=
+            ssl->s3->alpn_selected) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_ALPN_MISMATCH_ON_EARLY_DATA);
       return ssl_hs_error;
     }
@@ -781,8 +781,8 @@
     return 1;
   }
 
-  UniquePtr<SSL_SESSION> session(SSL_SESSION_dup(ssl->s3->established_session,
-                                                 SSL_SESSION_INCLUDE_NONAUTH));
+  UniquePtr<SSL_SESSION> session = SSL_SESSION_dup(
+      ssl->s3->established_session.get(), SSL_SESSION_INCLUDE_NONAUTH);
   if (!session) {
     return 0;
   }
diff --git a/src/ssl/tls13_server.cc b/src/ssl/tls13_server.cc
index 09437a5..89c9d46 100644
--- a/src/ssl/tls13_server.cc
+++ b/src/ssl/tls13_server.cc
@@ -371,8 +371,8 @@
       hs->new_session =
           SSL_SESSION_dup(session.get(), SSL_SESSION_DUP_AUTH_ONLY);
 
-      if (// Early data must be acceptable for this ticket.
-          ssl->cert->enable_early_data &&
+      if (ssl->cert->enable_early_data &&
+          // Early data must be acceptable for this ticket.
           session->ticket_max_early_data != 0 &&
           // The client must have offered early data.
           hs->early_data_offered &&
@@ -381,9 +381,8 @@
           // Custom extensions is incompatible with 0-RTT.
           hs->custom_extensions.received == 0 &&
           // The negotiated ALPN must match the one in the ticket.
-          ssl->s3->alpn_selected_len == session->early_alpn_len &&
-          OPENSSL_memcmp(ssl->s3->alpn_selected, session->early_alpn,
-                         ssl->s3->alpn_selected_len) == 0) {
+          ssl->s3->alpn_selected ==
+              MakeConstSpan(session->early_alpn, session->early_alpn_len)) {
         ssl->early_data_accepted = true;
       }
 
@@ -412,14 +411,14 @@
   hs->new_session->cipher = hs->new_cipher;
 
   // Store the initial negotiated ALPN in the session.
-  if (ssl->s3->alpn_selected != NULL) {
+  if (!ssl->s3->alpn_selected.empty()) {
     hs->new_session->early_alpn = (uint8_t *)BUF_memdup(
-        ssl->s3->alpn_selected, ssl->s3->alpn_selected_len);
+        ssl->s3->alpn_selected.data(), ssl->s3->alpn_selected.size());
     if (hs->new_session->early_alpn == NULL) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       return ssl_hs_error;
     }
-    hs->new_session->early_alpn_len = ssl->s3->alpn_selected_len;
+    hs->new_session->early_alpn_len = ssl->s3->alpn_selected.size();
   }
 
   if (ssl->ctx->dos_protection_cb != NULL &&
diff --git a/src/ssl/tls_method.cc b/src/ssl/tls_method.cc
index 94b9e20..c5b0178 100644
--- a/src/ssl/tls_method.cc
+++ b/src/ssl/tls_method.cc
@@ -73,14 +73,13 @@
   // The handshake should have released its final message.
   assert(!ssl->s3->has_message);
 
-  // During the handshake, |init_buf| is retained. Release if it there is no
+  // During the handshake, |hs_buf| is retained. Release if it there is no
   // excess in it. There may be excess left if there server sent Finished and
   // HelloRequest in the same record.
   //
   // TODO(davidben): SChannel does not support this. Reject this case.
-  if (ssl->init_buf != NULL && ssl->init_buf->length == 0) {
-    BUF_MEM_free(ssl->init_buf);
-    ssl->init_buf = NULL;
+  if (ssl->s3->hs_buf && ssl->s3->hs_buf->length == 0) {
+    ssl->s3->hs_buf.reset();
   }
 }
 
@@ -93,17 +92,13 @@
   }
 
   OPENSSL_memset(ssl->s3->read_sequence, 0, sizeof(ssl->s3->read_sequence));
-
-  Delete(ssl->s3->aead_read_ctx);
-  ssl->s3->aead_read_ctx = aead_ctx.release();
+  ssl->s3->aead_read_ctx = std::move(aead_ctx);
   return true;
 }
 
 static bool ssl3_set_write_state(SSL *ssl, UniquePtr<SSLAEADContext> aead_ctx) {
   OPENSSL_memset(ssl->s3->write_sequence, 0, sizeof(ssl->s3->write_sequence));
-
-  Delete(ssl->s3->aead_write_ctx);
-  ssl->s3->aead_write_ctx = aead_ctx.release();
+  ssl->s3->aead_write_ctx = std::move(aead_ctx);
   return true;
 }