Run the read pipe in SSLClientSocket immediately after the handshake.
See https://boringssl-review.googlesource.com/c/boringssl/+/37944 for
the rationale.
Bug: 950706, 958638
Change-Id: I221b0e7f6f626968fc38e0bce84260e4141c5e44
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1838456
Reviewed-by: Steven Valdez <[email protected]>
Commit-Queue: David Benjamin <[email protected]>
Cr-Commit-Position: refs/heads/master@{#702944}
diff --git a/net/socket/ssl_client_socket_impl.cc b/net/socket/ssl_client_socket_impl.cc
index 7e5b9dbb..afb45b5 100644
--- a/net/socket/ssl_client_socket_impl.cc
+++ b/net/socket/ssl_client_socket_impl.cc
@@ -16,6 +16,7 @@
#include "base/containers/span.h"
#include "base/feature_list.h"
#include "base/lazy_instance.h"
+#include "base/location.h"
#include "base/macros.h"
#include "base/memory/singleton.h"
#include "base/metrics/field_trial.h"
@@ -26,6 +27,7 @@
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/lock.h"
+#include "base/threading/sequenced_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
#include "base/values.h"
#include "build/build_config.h"
@@ -401,6 +403,7 @@
ssl_config_(ssl_config),
next_handshake_state_(STATE_NONE),
in_confirm_handshake_(false),
+ peek_complete_(false),
disconnected_(false),
negotiated_protocol_(kProtoUnknown),
certificate_requested_(false),
@@ -871,8 +874,10 @@
// Configure BoringSSL to allow renegotiations. Once the initial handshake
// completes, if renegotiations are not allowed, the default reject value will
// be restored. This is done in this order to permit a BoringSSL
- // optimization. See https://crbug.com/boringssl/123.
- SSL_set_renegotiate_mode(ssl_.get(), ssl_renegotiate_freely);
+ // optimization. See https://crbug.com/boringssl/123. Use
+ // ssl_renegotiate_explicit rather than ssl_renegotiate_freely so DoPeek()
+ // does not trigger renegotiations.
+ SSL_set_renegotiate_mode(ssl_.get(), ssl_renegotiate_explicit);
SSL_set_shed_handshake_config(ssl_.get(), 1);
@@ -1085,6 +1090,25 @@
completed_connect_ = true;
next_handshake_state_ = STATE_NONE;
+
+ // Read from the transport immediately after the handshake, whether Read() is
+ // called immediately or not. This serves several purposes:
+ //
+ // First, if this socket is preconnected and negotiates 0-RTT, the ServerHello
+ // will not be processed. See https://crbug.com/950706
+ //
+ // Second, in False Start and TLS 1.3, the tickets arrive after immediately
+ // after the handshake. This allows preconnected sockets to process the
+ // tickets sooner. This also avoids a theoretical deadlock if the tickets are
+ // too large. See
+ // https://boringssl-review.googlesource.com/c/boringssl/+/34948.
+ //
+ // TODO(https://crbug.com/958638): It is also a step in making TLS 1.3 client
+ // certificate alerts less unreliable.
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&SSLClientSocketImpl::DoPeek, weak_factory_.GetWeakPtr()));
+
return OK;
}
@@ -1320,22 +1344,29 @@
}
int total_bytes_read = 0;
- int ssl_ret;
+ int ssl_ret, ssl_err;
do {
ssl_ret = SSL_read(ssl_.get(), buf->data() + total_bytes_read,
buf_len - total_bytes_read);
- if (ssl_ret > 0)
+ ssl_err = SSL_get_error(ssl_.get(), ssl_ret);
+ if (ssl_ret > 0) {
total_bytes_read += ssl_ret;
+ } else if (ssl_err == SSL_ERROR_WANT_RENEGOTIATE) {
+ if (!SSL_renegotiate(ssl_.get())) {
+ ssl_err = SSL_ERROR_SSL;
+ }
+ }
// Continue processing records as long as there is more data available
// synchronously.
- } while (total_bytes_read < buf_len && ssl_ret > 0 &&
- transport_adapter_->HasPendingReadData());
+ } while (ssl_err == SSL_ERROR_WANT_RENEGOTIATE ||
+ (total_bytes_read < buf_len && ssl_ret > 0 &&
+ transport_adapter_->HasPendingReadData()));
// Although only the final SSL_read call may have failed, the failure needs to
// processed immediately, while the information still available in OpenSSL's
// error queue.
if (ssl_ret <= 0) {
- pending_read_ssl_error_ = SSL_get_error(ssl_.get(), ssl_ret);
+ pending_read_ssl_error_ = ssl_err;
if (pending_read_ssl_error_ == SSL_ERROR_ZERO_RETURN) {
pending_read_error_ = 0;
} else if (pending_read_ssl_error_ == SSL_ERROR_WANT_X509_LOOKUP &&
@@ -1419,6 +1450,22 @@
return net_error;
}
+void SSLClientSocketImpl::DoPeek() {
+ if (ssl_config_.disable_post_handshake_peek_for_testing ||
+ !completed_connect_ || peek_complete_) {
+ return;
+ }
+
+ crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
+
+ char byte;
+ int rv = SSL_peek(ssl_.get(), &byte, 1);
+ int ssl_err = SSL_get_error(ssl_.get(), rv);
+ if (ssl_err != SSL_ERROR_WANT_READ && ssl_err != SSL_ERROR_WANT_WRITE) {
+ peek_complete_ = true;
+ }
+}
+
void SSLClientSocketImpl::RetryAllOperations() {
// SSL_do_handshake, SSL_read, and SSL_write may all be retried when blocked,
// so retry all operations for simplicity. (Otherwise, SSL_get_error for each
@@ -1436,6 +1483,8 @@
if (!guard.get())
return;
+ DoPeek();
+
int rv_read = ERR_IO_PENDING;
int rv_write = ERR_IO_PENDING;
if (user_read_buf_) {