]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Detect fin state of a QUIC stream for streams which are completely read
authorNeil Horman <nhorman@openssl.org>
Thu, 5 Sep 2024 19:49:14 +0000 (15:49 -0400)
committerNeil Horman <nhorman@openssl.org>
Fri, 13 Sep 2024 19:05:29 +0000 (15:05 -0400)
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)

ssl/quic/quic_impl.c

index 539d6d9b7892a8e6e5597f17bff1bb2677c8b586..6d6592e9565ea9bccfd6a46e633257a969d58a32 100644 (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));