openssl / openssl/openssl

QUIC: incoming stream policy can become inconsistent with the accept queue

Open
#32,466 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

triaged: bug
Dominant language
C
Stars
30.8k
Forks
11.5k
Avg merge
10m
Merged PRs (30d)
1

Description

While reviewing the changes following #31685, I found several cases where OpenSSL gives conflicting answers about whether a stream created by the peer is available to the application.

I reproduced these cases on current master (b64f68a94e61fa2363c598c75444482b48056697).

Rejected bidirectional ordinal 0 can hide an accepted unidirectional ordinal 0
  1. Use explicit event handling and set the incoming policy to REJECT.
  2. Let the peer open bidirectional ordinal 0. It is rejected but remains temporarily in the stream map.
  3. Change the policy to ACCEPT.
  4. Let the peer open unidirectional ordinal 0 and send data. It enters the accept queue.
  5. Call SSL_read on the connection SSL object before a default stream exists.

quic_get_incoming_default_stream finds the rejected bidirectional object first and doesn't examine the queued unidirectional object. With explicit event handling, a nonblocking read reports SSL_ERROR_WANT_READ despite the available data. In implicit mode, an internal reactor tick may collect the rejected object and mask the lookup error, so the symptom can also depend on event handling mode and garbage collection timing.

AUTO can leave inaccessible streams in the accept queue

With the default AUTO policy, incoming streams are accepted while creation of a default stream remains possible. This case doesn't require a call to SSL_set_incoming_stream_policy. If the peer queues multiple streams, the first SSL_accept_stream call succeeds but also inhibits default stream creation. This changes the effective policy to REJECT.

The remaining streams stay in the accept queue, while subsequent SSL_accept_stream calls fail with ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED. SSL_get_accept_stream_queue_len and SSL_poll nevertheless continue to report them. A successful SSL_new_stream call can cause the same implicit AUTO transition while peer streams are queued.

The QUIC API design document says that a nonzero accept queue length guarantees that the next SSL_accept_stream call works. In this state, even a call without a stream direction filter fails.

ACCEPT to REJECT gives different results through different APIs

If a peer stream is queued under ACCEPT and the application changes the policy to REJECT, the stream remains queued:

  • SSL_accept_stream refuses to return it.
  • SSL_get_accept_stream_queue_len and SSL_poll report it.
  • SSL_read on the connection SSL object can still select it as the default stream.

The result depends on which public API is used to access the same stream.

Documentation and design don't select a remedy

The public SSL_set_incoming_stream_policy manual currently describes AUTO as REJECT from the outset under the automatic default stream modes. The implementation instead starts at ACCEPT and changes to REJECT only after a default stream is created or automatic creation is inhibited. This factual documentation error can be corrected independently.

The internal QUIC API design document says both that an effective REJECT makes SSL_accept_stream return NULL and that a nonzero queue length guarantees the next accept succeeds. Those statements can't both hold in the states described above. This supports fixing the inconsistency, but doesn't require draining the queue. The same document predates the current stream direction flags and says the accept flags are unused, while the public manual allows a filtered accept to return NULL with other streams still queued.

Expected behavior

The effective incoming stream policy, accept queue accounting, poll readiness, SSL_accept_stream, and default stream selection should give a consistent view of an incoming stream. In particular, a stream shouldn't remain reported as available when it can't be obtained through the corresponding accept API, and policy enforcement shouldn't depend on which API is used to access it.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the reported policy and accept-queue cases on current master, tracing quic_get_incoming_default_stream alongside SSL_accept_stream, SSL_get_accept_stream_queue_len, SSL_poll, SSL_read, and SSL_new_stream. Read the QUIC API design document and the SSL_set_incoming_stream_policy manual entry. Done means effective policy, queue accounting, readiness, accept behavior, and default-stream selection present one consistent view.

Written by the indexing model from the issue text.

Assessment

Tech stack
c
Domain
api, networking
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.