In SSL_reset_early_data_reject, clear out unreported bytes written.

This fixes initial content in case 0-RTT gets rejected after trying to
send a large amount of data.

Change-Id: Ie7be08a9ac050153db43877552923ba26a6a6964
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/95547
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: Rudolf Polzer <rpolzer@google.com>
diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc
index bc319b3..29ac089 100644
--- a/ssl/ssl_lib.cc
+++ b/ssl/ssl_lib.cc
@@ -1146,6 +1146,7 @@
   // retry. The handshake will transparently flush out the pending record
   // (discarded by the server) to keep the framing correct.
   ssl->s3->pending_write = {};
+  ssl->s3->unreported_bytes_written = 0;
 }
 
 enum ssl_early_data_reason_t SSL_get_early_data_reason(const SSL *ssl) {
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index 284c5ac..0a7c900 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -3847,6 +3847,77 @@
   EXPECT_EQ(0u, len);
 }
 
+TEST(SSLTest, EarlyDataRejectStaleUnreportedBytes) {
+  bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
+  ASSERT_TRUE(client_ctx);
+  bssl::UniquePtr<SSL_CTX> server_ctx =
+      CreateContextWithTestCertificate(TLS_method());
+  ASSERT_TRUE(server_ctx);
+
+  // Setup a 0-RTT session.
+  SSL_CTX_set_early_data_enabled(client_ctx.get(), 1);
+  SSL_CTX_set_early_data_enabled(server_ctx.get(), 1);
+  SSL_CTX_set_session_cache_mode(client_ctx.get(), SSL_SESS_CACHE_BOTH);
+  SSL_CTX_set_session_cache_mode(server_ctx.get(), SSL_SESS_CACHE_BOTH);
+  SSL_CTX_set_custom_verify(client_ctx.get(), SSL_VERIFY_PEER,
+                            AcceptAnyCertificate);
+  bssl::UniquePtr<SSL_SESSION> session =
+      CreateClientSession(client_ctx.get(), server_ctx.get());
+  ASSERT_TRUE(session);
+  ASSERT_TRUE(SSL_SESSION_early_data_capable(session.get()));
+
+  // Use the session, but with 0-RTT disabled on the server.
+  bssl::UniquePtr<SSL> client, server;
+  ASSERT_TRUE(CreateClientAndServer(&client, &server, client_ctx.get(),
+                                    server_ctx.get()));
+  SSL_set_session(client.get(), session.get());
+  SSL_set_early_data_enabled(server.get(), 0);
+
+  // The client sends ClientHello.
+  ASSERT_EQ(1, SSL_do_handshake(client.get()));
+  ASSERT_TRUE(SSL_in_early_data(client.get()));
+
+  // Try to write a large amount of early data, large enough that one record
+  // gets through, but a subsequent record is interrupted. (In this case, by the
+  // early data limit, but the exact interrupt is not important.)
+  //
+  // |client| now remembers there is a pending write, and that some prefix of it
+  // has already been written.
+  std::vector<uint8_t> early_write(20000, 'A');
+  ASSERT_EQ(-1,
+            SSL_write(client.get(), early_write.data(), early_write.size()));
+  ASSERT_EQ(SSL_ERROR_WANT_READ, SSL_get_error(client.get(), -1));
+
+  // The server reads the ClientHello, rejects 0-RTT, and sends
+  // ServerHello..Finished.
+  ASSERT_EQ(-1, SSL_do_handshake(server.get()));
+  ASSERT_EQ(SSL_ERROR_WANT_READ, SSL_get_error(server.get(), -1));
+  ASSERT_FALSE(SSL_in_early_data(server.get()));
+
+  // The client retries the write. It now reads the server's flight and
+  // discovers 0-RTT was rejected.
+  ASSERT_EQ(-1,
+            SSL_write(client.get(), early_write.data(), early_write.size()));
+  ASSERT_EQ(SSL_ERROR_EARLY_DATA_REJECTED, SSL_get_error(client.get(), -1));
+
+  // Resetting the early data reject should allow the handshake to proceed and
+  // forget the pending write.
+  SSL_reset_early_data_reject(client.get());
+  ASSERT_TRUE(CompleteHandshakes(client.get(), server.get()));
+  ASSERT_FALSE(SSL_in_early_data(client.get()));
+
+  // Test the pending write was cleared by attempting to write something else.
+  static const uint8_t kNewWrite[] = {'h', 'e', 'l', 'l', 'o'};
+  ASSERT_EQ(static_cast<int>(sizeof(kNewWrite)),
+            SSL_write(client.get(), kNewWrite, sizeof(kNewWrite)));
+
+  // Confirm it was written correctly.
+  uint8_t received[sizeof(kNewWrite)];
+  ASSERT_EQ(static_cast<int>(sizeof(received)),
+            SSL_read(server.get(), received, sizeof(received)));
+  EXPECT_EQ(Bytes(received), Bytes(kNewWrite));
+}
+
 TEST(SSLTest, SessionDuplication) {
   bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
   bssl::UniquePtr<SSL_CTX> server_ctx =