Skip to content

RFC 9000 Section 10.1 idle-timeout immediate-close commitment recheck #3256

Description

@LiD0209

RFC 9000 Section 10.1 idle-timeout immediate-close commitment recheck

  • Real but low-severity edge deviation: s2n-quic has paths that abandon the connection before the effective idle timeout without sending CONNECTION_CLOSE (PTO backoff overflow, supervisor ImmediateClose, and max handshake duration exceeded).
  • Normal paths are compliant: explicit application close, handle drop, idle timeout expiry, and no-valid-path behavior align with RFC 9000 Section 10.1.
  • The original report's static match to path/ecn.rs was unrelated to idle timeout. This revised report keeps the concern, but bases it on the correct source paths and runtime observations.

Standard Requirement

If either endpoint specifies max_idle_timeout, the connection is silently closed and state is discarded when it remains idle longer than the minimum value advertised by both endpoints. By advertising max_idle_timeout, an endpoint commits to initiating an immediate close if it abandons the connection before the effective value.

Silent close is allowed in two relevant cases: when the idle timeout expires, and when there is no validated path on which to send. Other abandonment before the effective idle timeout must initiate immediate close under Section 10.2. Section 10.2.3 only specifies which packet protection level should carry CONNECTION_CLOSE during the handshake; it does not exempt silent close.

Code Review

  • Frame generation in quic/s2n-quic-core/src/connection/error.rs (as_frame, around lines 467-468) produces no CONNECTION_CLOSE for:

    Error::MaxHandshakeDurationExceeded { .. } => None,
    Error::ImmediateClose { .. } => None,

    IdleTimerExpired and NoValidPath are also None, but those are standard-permitted silent-close cases.

  • The error type documents ImmediateClose as:

    /// The connection should be closed immediately without notifying the peer
    ImmediateClose { reason: &'static str, ... },
  • Silent abandonment path 1: PTO backoff overflow in quic/s2n-quic-transport/src/space/mod.rs:

    let max_backoff = path.pto_backoff.checked_mul(2).ok_or_else(|| {
        connection::Error::immediate_close("PTO backoff multiplier exceeded maximum value")
    })?;
  • Silent abandonment path 2: supervisor event in quic/s2n-quic-core/src/event/generated.rs:

    /// Close the connection without notifying the peer
    ImmediateClose { reason: &'static str },

    After on_supervisor_timeout returns this result, quic/s2n-quic-transport/src/connection/connection_impl.rs returns Err(connection::Error::immediate_close(reason)).

  • State transitions in connection_impl.rs send a local active close to Closing; IdleTimerExpired and NoValidPath go to Finished, which is permitted; ImmediateClose and other catch-all errors also go to Finished.

  • The compliant comparison path is explicit application Trait::close() or handle drop, which goes through application_close -> local Closed / Application -> as_frame emits CONNECTION_CLOSE.

Runtime Evidence

I ran the end-to-end simulation test:

cargo test -p s2n-quic-tests tests::connection_migration::pto_backoff_exceeding_max_value_closes_connection

The test passed. The observed behavior was that the server closed with ImmediateClose after PTO overflow, using reason PTO backoff multiplier exceeded maximum value. The client never received CONNECTION_CLOSE and eventually ended with IdleTimerExpired. That is direct evidence of abandonment before the effective timeout without peer notification.

I also temporarily added a unit probe that called as_frame directly. It observed:

  • Error::closed(Local) -> Some, producing CONNECTION_CLOSE;
  • Error::immediate_close(...) -> None, producing no frame;
  • Error::idle_timer_expired() -> None, which is a permitted silent close.

The probe passed, cargo check -p s2n-quic-core passed afterward, and the source was restored to the upstream v1.85.0 state.

Final Assessment

The fact pattern is real: there are paths that silently abandon the connection before idle timeout. They are low-severity edge cases. PTO overflow requires repeated backoff toward the upper bound while the peer remains unresponsive; supervisor ImmediateClose is a documented application choice not to notify the peer; max handshake duration exceeded occurs only during the handshake.

The original direction was plausible, but the evidence chain was wrong. This revised report keeps the concern and corrects the rationale to the silent-close paths above rather than the unrelated ECN match.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions