Skip to content

Commit 41914a4

Browse files
wang178ccopybara-github
authored andcommitted
Add QuicServerSessionBase::enable_reset_ssl_after_handshake that reset SSL after handshake is done to save connection level memory on the server side.
PiperOrigin-RevId: 856230801
1 parent 649bff3 commit 41914a4

File tree

9 files changed

+124
-4
lines changed

9 files changed

+124
-4
lines changed

quiche/quic/core/crypto/tls_connection.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,12 @@ class QUICHE_EXPORT TlsConnection {
107107

108108
SSL* ssl() const { return ssl_.get(); }
109109

110+
// ResetSsl can be called to reset the SSL and release the associated memory.
111+
// After this is called, ssl() will return nullptr. Hence it should only be
112+
// called when there is absolutely no expectation that ssl() will be called
113+
// again.
114+
void ResetSsl() { ssl_.reset(); }
115+
110116
const QuicSSLConfig& ssl_config() const { return ssl_config_; }
111117

112118
protected:

quiche/quic/core/http/quic_server_session_base.cc

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
#include <string>
1313
#include <utility>
1414

15+
#include "quiche/quic/core/frames/quic_crypto_frame.h"
16+
#include "quiche/quic/core/frames/quic_frame.h"
17+
#include "quiche/quic/core/http/quic_spdy_session.h"
1518
#include "quiche/quic/core/proto/cached_network_parameters_proto.h"
1619
#include "quiche/quic/core/quic_connection.h"
1720
#include "quiche/quic/core/quic_stream.h"
@@ -23,6 +26,7 @@
2326
#include "quiche/quic/platform/api/quic_flag_utils.h"
2427
#include "quiche/quic/platform/api/quic_flags.h"
2528
#include "quiche/quic/platform/api/quic_logging.h"
29+
#include "quiche/common/platform/api/quiche_flag_utils.h"
2630
#include "quiche/common/platform/api/quiche_logging.h"
2731

2832
namespace quic {
@@ -304,6 +308,38 @@ QuicSSLConfig QuicServerSessionBase::GetSSLConfig() const {
304308
return ssl_config;
305309
}
306310

311+
bool QuicServerSessionBase::OnFrameAcked(const quic::QuicFrame& frame,
312+
quic::QuicTime::Delta ack_delay_time,
313+
quic::QuicTime receive_timestamp,
314+
bool is_retransmission) {
315+
bool result = QuicSpdySession::OnFrameAcked(
316+
frame, ack_delay_time, receive_timestamp, is_retransmission);
317+
if (!reset_ssl_after_handshake_ || ssl_reset_) {
318+
return result;
319+
}
320+
if (frame.type == quic::HANDSHAKE_DONE_FRAME) {
321+
handshake_done_acked_ = true;
322+
}
323+
// When resumption is enabled, the resumption tickets must have been
324+
// buffered at the time the handshake_done frame is ACKed.
325+
if (handshake_done_acked_ && !HasUnackedCryptoData()) {
326+
QUICHE_CODE_COUNT(quic_reset_ssl_after_handshake);
327+
crypto_stream_->ResetSsl();
328+
ssl_reset_ = true;
329+
}
330+
return result;
331+
}
332+
333+
void QuicServerSessionBase::OnCryptoFrame(const quic::QuicCryptoFrame& frame) {
334+
if (ssl_reset_) {
335+
// This code path can happen due to retransmission of crypto data but
336+
// should be rare. Add a code count to monitor.
337+
QUICHE_CODE_COUNT(quic_reset_ssl_after_handshake);
338+
return;
339+
}
340+
QuicSpdySession::OnCryptoFrame(frame);
341+
}
342+
307343
std::optional<CachedNetworkParameters>
308344
QuicServerSessionBase::GenerateCachedNetworkParameters() const {
309345
const QuicSentPacketManager& sent_packet_manager =

quiche/quic/core/http/quic_server_session_base.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@
1414
#include <vector>
1515

1616
#include "quiche/quic/core/crypto/quic_compressed_certs_cache.h"
17+
#include "quiche/quic/core/frames/quic_crypto_frame.h"
18+
#include "quiche/quic/core/frames/quic_frame.h"
1719
#include "quiche/quic/core/http/quic_spdy_session.h"
1820
#include "quiche/quic/core/quic_crypto_server_stream_base.h"
1921
#include "quiche/quic/core/quic_packets.h"
22+
#include "quiche/quic/core/quic_time.h"
2023
#include "quiche/quic/platform/api/quic_export.h"
2124

2225
namespace quic {
@@ -73,6 +76,15 @@ class QUICHE_EXPORT QuicServerSessionBase : public QuicSpdySession {
7376

7477
QuicSSLConfig GetSSLConfig() const override;
7578

79+
// Override to reset the SSL when HANDSHAKE_DONE frame has been ACKed and
80+
// there is no unacked crypto data.
81+
bool OnFrameAcked(const QuicFrame& frame, QuicTime::Delta ack_delay_time,
82+
QuicTime receive_timestamp,
83+
bool is_retransmission) override;
84+
85+
// Override to ignore the crypto frame after resetting the SSL.
86+
void OnCryptoFrame(const QuicCryptoFrame& frame) override;
87+
7688
protected:
7789
// QuicSession methods(override them with return type of QuicSpdyStream*):
7890
QuicCryptoServerStreamBase* GetMutableCryptoStream() override;
@@ -102,6 +114,12 @@ class QUICHE_EXPORT QuicServerSessionBase : public QuicSpdySession {
102114

103115
QuicCryptoServerStreamBase::Helper* stream_helper() { return helper_; }
104116

117+
void enable_reset_ssl_after_handshake() {
118+
if (version().IsIetfQuic()) {
119+
reset_ssl_after_handshake_ = true;
120+
}
121+
}
122+
105123
private:
106124
friend class test::QuicServerSessionBasePeer;
107125
friend class test::QuicSimpleServerSessionPeer;
@@ -116,6 +134,12 @@ class QUICHE_EXPORT QuicServerSessionBase : public QuicSpdySession {
116134
// Whether bandwidth resumption is enabled for this connection.
117135
bool bandwidth_resumption_enabled_;
118136

137+
// Whether to reset the SSL after the handshake is done.
138+
bool reset_ssl_after_handshake_ = false;
139+
// Whether the SSL has been reset.
140+
bool ssl_reset_ = false;
141+
bool handshake_done_acked_ = false;
142+
119143
// The cache which contains most recently compressed certs.
120144
// Owned by QuicDispatcher.
121145
QuicCompressedCertsCache* compressed_certs_cache_;

quiche/quic/core/quic_crypto_server_stream.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "quiche/quic/core/quic_session.h"
1515
#include "quiche/quic/core/quic_types.h"
1616
#include "quiche/quic/platform/api/quic_export.h"
17+
#include "quiche/common/platform/api/quiche_logging.h"
1718

1819
namespace quic {
1920

@@ -56,6 +57,7 @@ class QUICHE_EXPORT QuicCryptoServerStream : public QuicCryptoServerStreamBase,
5657
bool ShouldSendExpectCTHeader() const override;
5758
bool DidCertMatchSni() const override;
5859
const ProofSource::Details* ProofSourceDetails() const override;
60+
void ResetSsl() override { QUICHE_DCHECK(false); }
5961

6062
// From QuicCryptoStream
6163
ssl_early_data_reason_t EarlyDataReason() const override;

quiche/quic/core/quic_crypto_server_stream_base.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ class QUICHE_EXPORT QuicCryptoServerStreamBase : public QuicCryptoStream {
105105
QUICHE_NOTREACHED();
106106
return false;
107107
}
108+
109+
// ResetSsl can be called to reset the SSL and release the associated memory.
110+
// After this is called, GetSsl() should return nullptr.
111+
virtual void ResetSsl() = 0;
108112
};
109113

110114
// Creates an appropriate QuicCryptoServerStream for the provided parameters,

quiche/quic/core/tls_handshaker.cc

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,10 @@ bool TlsHandshaker::SetReadSecret(EncryptionLevel level,
348348
/*latch_once_used=*/false);
349349
}
350350

351+
const SSL_CIPHER* TlsHandshaker::GetCipher() const {
352+
return SSL_get_current_cipher(ssl());
353+
}
354+
351355
std::unique_ptr<QuicDecrypter>
352356
TlsHandshaker::AdvanceKeysAndCreateCurrentOneRttDecrypter() {
353357
if (latest_read_secret_.empty() || latest_write_secret_.empty() ||
@@ -358,7 +362,7 @@ TlsHandshaker::AdvanceKeysAndCreateCurrentOneRttDecrypter() {
358362
CloseConnection(QUIC_INTERNAL_ERROR, error_details);
359363
return nullptr;
360364
}
361-
const SSL_CIPHER* cipher = SSL_get_current_cipher(ssl());
365+
const SSL_CIPHER* cipher = GetCipher();
362366
const EVP_MD* prf = Prf(cipher);
363367
CryptoUtils::GenerateNextKeyPhaseSecret(
364368
prf, handshaker_delegate_->parsed_version(), latest_read_secret_,
@@ -387,7 +391,7 @@ std::unique_ptr<QuicEncrypter> TlsHandshaker::CreateCurrentOneRttEncrypter() {
387391
CloseConnection(QUIC_INTERNAL_ERROR, error_details);
388392
return nullptr;
389393
}
390-
const SSL_CIPHER* cipher = SSL_get_current_cipher(ssl());
394+
const SSL_CIPHER* cipher = GetCipher();
391395
std::unique_ptr<QuicEncrypter> encrypter =
392396
QuicEncrypter::CreateFromCipherSuite(SSL_CIPHER_get_id(cipher));
393397
CryptoUtils::SetKeyAndIV(Prf(cipher), latest_write_secret_,

quiche/quic/core/tls_handshaker.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ class QUICHE_EXPORT TlsHandshaker : public TlsConnection::Delegate,
177177
extra_error_details_ = std::move(extra_error_details);
178178
}
179179

180+
virtual const SSL_CIPHER* GetCipher() const;
181+
180182
private:
181183
// ProofVerifierCallbackImpl handles the result of an asynchronous certificate
182184
// verification operation.

quiche/quic/core/tls_server_handshaker.cc

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,10 +344,16 @@ bool TlsServerHandshaker::DisableResumption() {
344344
}
345345

346346
bool TlsServerHandshaker::IsZeroRtt() const {
347+
if (cached_ssl_info_.has_value()) {
348+
return cached_ssl_info_->is_zero_rtt;
349+
}
347350
return SSL_early_data_accepted(ssl());
348351
}
349352

350353
bool TlsServerHandshaker::IsResumption() const {
354+
if (cached_ssl_info_.has_value()) {
355+
return cached_ssl_info_->is_resumption;
356+
}
351357
return SSL_session_reused(ssl());
352358
}
353359

@@ -448,6 +454,9 @@ void TlsServerHandshaker::OnConnectionClosed(
448454
}
449455

450456
ssl_early_data_reason_t TlsServerHandshaker::EarlyDataReason() const {
457+
if (cached_ssl_info_.has_value()) {
458+
return cached_ssl_info_->early_data_reason;
459+
}
451460
return TlsHandshaker::EarlyDataReason();
452461
}
453462

@@ -656,7 +665,7 @@ void TlsServerHandshaker::SetWriteSecret(
656665
if (level == ENCRYPTION_FORWARD_SECURE) {
657666
encryption_established_ = true;
658667
// Fill crypto_negotiated_params_:
659-
const SSL_CIPHER* ssl_cipher = SSL_get_current_cipher(ssl());
668+
const SSL_CIPHER* ssl_cipher = GetCipher();
660669
if (ssl_cipher) {
661670
crypto_negotiated_params_->cipher_suite =
662671
SSL_CIPHER_get_protocol_id(ssl_cipher);
@@ -1230,7 +1239,7 @@ bool TlsServerHandshaker::WillNotCallComputeSignature() const {
12301239
}
12311240

12321241
std::optional<uint16_t> TlsServerHandshaker::GetCiphersuite() const {
1233-
const SSL_CIPHER* cipher = SSL_get_pending_cipher(ssl());
1242+
const SSL_CIPHER* cipher = GetCipher();
12341243
if (cipher == nullptr) {
12351244
return std::nullopt;
12361245
}
@@ -1343,6 +1352,20 @@ TlsServerHandshaker::SetApplicationSettings(absl::string_view alpn) {
13431352

13441353
SSL* TlsServerHandshaker::GetSsl() const { return ssl(); }
13451354

1355+
void TlsServerHandshaker::ResetSsl() {
1356+
if (cached_ssl_info_.has_value()) {
1357+
QUIC_BUG(quic_bug_ssl_is_reset_again);
1358+
return;
1359+
}
1360+
cached_ssl_info_.emplace(CachedSSLInfo{
1361+
.is_resumption = IsResumption(),
1362+
.is_zero_rtt = IsZeroRtt(),
1363+
.early_data_reason = EarlyDataReason(),
1364+
.cipher = GetCipher(),
1365+
});
1366+
tls_connection_.ResetSsl();
1367+
}
1368+
13461369
bool TlsServerHandshaker::IsCryptoFrameExpectedForEncryptionLevel(
13471370
EncryptionLevel level) const {
13481371
return level != ENCRYPTION_ZERO_RTT;

quiche/quic/core/tls_server_handshaker.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ class QUICHE_EXPORT TlsServerHandshaker : public TlsHandshaker,
9191
bool ExportKeyingMaterial(absl::string_view label, absl::string_view context,
9292
size_t result_len, std::string* result) override;
9393
SSL* GetSsl() const override;
94+
void ResetSsl() override;
9495
bool IsCryptoFrameExpectedForEncryptionLevel(
9596
EncryptionLevel level) const override;
9697
EncryptionLevel GetEncryptionLevelToSendCryptoDataOfSpace(
@@ -223,6 +224,13 @@ class QUICHE_EXPORT TlsServerHandshaker : public TlsHandshaker,
223224

224225
void SetIgnoreTicketOpen(bool value) { ignore_ticket_open_ = value; }
225226

227+
const SSL_CIPHER* GetCipher() const override {
228+
if (cached_ssl_info_.has_value()) {
229+
return cached_ssl_info_->cipher;
230+
}
231+
return TlsHandshaker::GetCipher();
232+
}
233+
226234
private:
227235
class QUICHE_EXPORT DecryptCallback : public ProofSource::DecryptCallback {
228236
public:
@@ -404,6 +412,17 @@ class QUICHE_EXPORT TlsServerHandshaker : public TlsHandshaker,
404412

405413
std::unique_ptr<ApplicationState> application_state_;
406414

415+
// Used to cache the state of the SSL object after it is reset.
416+
struct CachedSSLInfo {
417+
bool is_resumption = false;
418+
bool is_zero_rtt = false;
419+
ssl_early_data_reason_t early_data_reason = ssl_early_data_unknown;
420+
// Note SSL_get_current_cipher returns a static allocated pointer and as a
421+
// result it is safe to cache a raw pointer here.
422+
const SSL_CIPHER* cipher = nullptr;
423+
};
424+
std::optional<CachedSSLInfo> cached_ssl_info_;
425+
407426
// Pre-shared key used during the handshake.
408427
std::string pre_shared_key_;
409428

0 commit comments

Comments
 (0)