In quiche 0.29.1 and 0.29.2, a transport error detected while processing the peer’s first flight can be recorded as a local connection error, but then made unsendable before send() can emit a peer-processable CONNECTION_CLOSE.
One concrete case is the existing missing_initial_source_connection_id test. The server detects Error::InvalidTransportParam, records a local error, and then marks the connection closed because recv_count == 0. A later send() therefore returns without emitting the pending transport close.
Minimal reproduction
Extend the existing missing_initial_source_connection_id test in quiche/src/tests.rs:
assert_eq!(
pipe.server_recv(&mut buf[..len]),
Err(Error::InvalidTransportParam)
);
assert!(pipe.server.local_error().is_some());
assert!(pipe.server.send(&mut buf).is_ok());
Run:
cargo test -p quiche missing_initial_source_connection_id -- --nocapture
Observed behavior
On unmodified quiche 0.29.1 and 0.29.2:
server_recv() returns Err(Error::InvalidTransportParam).
server.local_error() is set.
Connection::close() marks the connection closed because recv_count == 0.
- A subsequent
send() cannot serialize the recorded close.
If the recv_count == 0 early-close path is relaxed, send() can still select a Handshake packet while the TLS write level maps to packet::Epoch::Application and the handshake is not confirmed. In this first-flight error case, Initial keys are still available, and selecting Handshake can fail instead of emitting an Initial CONNECTION_CLOSE.
Expected behavior
After the server has processed the Initial far enough to detect and record a transport-parameter connection error, send() should be able to emit a peer-processable transport CONNECTION_CLOSE, when Initial sealing keys are available.
For the missing_initial_source_connection_id case, the emitted close should be an Initial packet carrying a transport-level CONNECTION_CLOSE with error code TRANSPORT_PARAMETER_ERROR (0x08).
Minimal patch demonstrating the issue
diff quiche/src/lib.rs
- if self.recv_count == 0 {
+ if self.recv_count == 0 && !self.derived_initial_secrets {
self.mark_closed();
}
if !self.handshake_confirmed {
match epoch {
+ packet::Epoch::Application
+ if self.crypto_ctx[packet::Epoch::Initial].has_keys() =>
+ return Ok(Type::Initial),
packet::Epoch::Application => return Ok(Type::Handshake),
With this change, the focused reproduction can emit an Initial transport CONNECTION_CLOSE instead of making the recorded local error unsendable.
This patch is intended as a minimal demonstration of the failing state transition, not necessarily as the final upstream design.
RFC basis
RFC 9000 §7.3 requires the initial_source_connection_id transport parameter to be present. If it is absent, the endpoint MUST close the connection with TRANSPORT_PARAMETER_ERROR.
RFC 9000 §18.2 defines server-only transport parameters. If a client sends a server-only transport parameter, the server MUST treat that as a connection error of type TRANSPORT_PARAMETER_ERROR.
RFC 9000 §17.2 requires nonzero reserved bits in a protected long-header packet, after packet and header protection are removed, to be treated as a connection error of type PROTOCOL_VIOLATION.
RFC 9000 §17.3.1 requires nonzero reserved bits in a protected short-header packet, after packet and header protection are removed, to be treated as a connection error of type PROTOCOL_VIOLATION.
Relevant RFC sections:
In quiche 0.29.1 and 0.29.2, a transport error detected while processing the peer’s first flight can be recorded as a local connection error, but then made unsendable before
send()can emit a peer-processableCONNECTION_CLOSE.One concrete case is the existing
missing_initial_source_connection_idtest. The server detectsError::InvalidTransportParam, records a local error, and then marks the connection closed becauserecv_count == 0. A latersend()therefore returns without emitting the pending transport close.Minimal reproduction
Extend the existing
missing_initial_source_connection_idtest inquiche/src/tests.rs:Run:
cargo test -p quiche missing_initial_source_connection_id -- --nocaptureObserved behavior
On unmodified quiche 0.29.1 and 0.29.2:
server_recv()returnsErr(Error::InvalidTransportParam).server.local_error()is set.Connection::close()marks the connection closed becauserecv_count == 0.send()cannot serialize the recorded close.If the
recv_count == 0early-close path is relaxed,send()can still select a Handshake packet while the TLS write level maps topacket::Epoch::Applicationand the handshake is not confirmed. In this first-flight error case, Initial keys are still available, and selecting Handshake can fail instead of emitting an InitialCONNECTION_CLOSE.Expected behavior
After the server has processed the Initial far enough to detect and record a transport-parameter connection error,
send()should be able to emit a peer-processable transportCONNECTION_CLOSE, when Initial sealing keys are available.For the
missing_initial_source_connection_idcase, the emitted close should be an Initial packet carrying a transport-levelCONNECTION_CLOSEwith error codeTRANSPORT_PARAMETER_ERROR(0x08).Minimal patch demonstrating the issue
With this change, the focused reproduction can emit an Initial transport
CONNECTION_CLOSEinstead of making the recorded local error unsendable.This patch is intended as a minimal demonstration of the failing state transition, not necessarily as the final upstream design.
RFC basis
RFC 9000 §7.3 requires the
initial_source_connection_idtransport parameter to be present. If it is absent, the endpoint MUST close the connection withTRANSPORT_PARAMETER_ERROR.RFC 9000 §18.2 defines server-only transport parameters. If a client sends a server-only transport parameter, the server MUST treat that as a connection error of type
TRANSPORT_PARAMETER_ERROR.RFC 9000 §17.2 requires nonzero reserved bits in a protected long-header packet, after packet and header protection are removed, to be treated as a connection error of type
PROTOCOL_VIOLATION.RFC 9000 §17.3.1 requires nonzero reserved bits in a protected short-header packet, after packet and header protection are removed, to be treated as a connection error of type
PROTOCOL_VIOLATION.Relevant RFC sections: