Detect fin state of a QUIC stream for streams which are completely read

SSL_poll indicates that a stream which has had the fin bit set on it,
should generate SSL_POLL_EVENT_R events, so that applications can detect
stream completion via SSL_read_ex and SSL_get_error returning
SSL_ERROR_ZERO_RETURN.

However, the quic polling code misses on this, as a client that
completely reads a buffer after receipt has its underlying stream buffer
freed, loosing the fin status

We can however detect stream completion still, as a stream which has
been finalized, and had all its data read will be in the
QUIC_RSTREAM_STATE_DATA_READ state, iff the fin bit was set.

Fix it by checking in test_poll_event_r for that state, and generating a
SSL_POLL_EVENT_R if its found to be true, so as to stay in line with the
docs.

Fixes openssl/private#627

Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Sasa Nedvedicky <sashan@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/25399)
This commit is contained in:
Neil Horman 2024-09-05 15:49:14 -04:00
parent 8e0d479b98
commit c8127df04c

View file

@ -4023,6 +4023,22 @@ static int test_poll_event_r(QUIC_XSO *xso)
int fin = 0;
size_t avail = 0;
/*
* If a stream has had the fin bit set on the last packet
* received, then we need to return a 1 here to raise
* SSL_POLL_EVENT_R, so that the stream can have its completion
* detected and closed gracefully by an application.
* However, if the client reads the data via SSL_read[_ex], that api
* provides no stream status, and as a result the stream state moves to
* QUIC_RSTREAM_STATE_DATA_READ, and the receive buffer is freed, which
* stored the fin state, so its not directly know-able here. Instead
* check for the stream state being QUIC_RSTREAM_STATE_DATA_READ, which
* is only set if the last stream frame received had the fin bit set, and
* the client read the data. This catches our poll/read/poll case
*/
if (xso->stream->recv_state == QUIC_RSTREAM_STATE_DATA_READ)
return 1;
return ossl_quic_stream_has_recv_buffer(xso->stream)
&& ossl_quic_rstream_available(xso->stream->rstream, &avail, &fin)
&& (avail > 0 || (fin && !xso->retired_fin));