]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
QUIC CONFORMANCE: Handle RESET_STREAM final size correctly
authorHugo Landau <hlandau@openssl.org>
Tue, 6 Jun 2023 15:25:11 +0000 (16:25 +0100)
committerPauli <pauli@openssl.org>
Sun, 16 Jul 2023 22:17:57 +0000 (08:17 +1000)
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/21135)

ssl/quic/quic_stream_map.c
ssl/quic/quic_txp.c

index ccd54edb4e6a211aac6a14090676b57da6e4d03f..6fac30c243d15e6026012976b8c57f737696831f 100644 (file)
@@ -416,8 +416,6 @@ int ossl_quic_stream_map_reset_stream_send_part(QUIC_STREAM_MAP *qsm,
                                                 QUIC_STREAM *qs,
                                                 uint64_t aec)
 {
-    uint64_t final_size;
-
     switch (qs->send_state) {
     default:
     case QUIC_SSTREAM_STATE_NONE:
@@ -435,7 +433,6 @@ int ossl_quic_stream_map_reset_stream_send_part(QUIC_STREAM_MAP *qsm,
 
     case QUIC_SSTREAM_STATE_READY:
     case QUIC_SSTREAM_STATE_SEND:
-    case QUIC_SSTREAM_STATE_DATA_SENT:
         /*
          * If we already have a final size (e.g. because we are coming from
          * DATA_SENT), we have to be consistent with that, so don't change it.
@@ -447,9 +444,10 @@ int ossl_quic_stream_map_reset_stream_send_part(QUIC_STREAM_MAP *qsm,
          * transmitted yet, to avoid unnecessarily consuming flow control
          * credit. We can get this from the TXFC.
          */
-        if (!ossl_quic_stream_send_get_final_size(qs, &final_size))
-            qs->send_final_size = ossl_quic_txfc_get_swm(&qs->txfc);
+        qs->send_final_size = ossl_quic_txfc_get_swm(&qs->txfc);
 
+        /* FALLTHROUGH */
+    case QUIC_SSTREAM_STATE_DATA_SENT:
         qs->reset_stream_aec    = aec;
         qs->want_reset_stream   = 1;
         qs->send_state          = QUIC_SSTREAM_STATE_RESET_SENT;
@@ -640,15 +638,24 @@ int ossl_quic_stream_map_stop_sending_recv_part(QUIC_STREAM_MAP *qsm,
          */
     case QUIC_RSTREAM_STATE_RESET_RECVD:
     case QUIC_RSTREAM_STATE_RESET_READ:
-        /* No point in STOP_SENDING if the peer already reset their send part. */
+        /*
+         * RFC 9000 s. 3.5: "STOP_SENDING SHOULD only be sent for a stream that
+         * has not been reset by the peer."
+         *
+         * No point in STOP_SENDING if the peer already reset their send part.
+         */
         return 0;
 
     case QUIC_RSTREAM_STATE_RECV:
     case QUIC_RSTREAM_STATE_SIZE_KNOWN:
         /*
-         * It does make sense to send STOP_SENDING for a receive part of a
-         * stream which has a known size (because we have received a FIN) but
-         * which still has other (previous) stream data yet to be received.
+         * RFC 9000 s. 3.5: "If the stream is in the Recv or Size Known state,
+         * the transport SHOULD signal this by sending a STOP_SENDING frame to
+         * prompt closure of the stream in the opposite direction."
+         *
+         * Note that it does make sense to send STOP_SENDING for a receive part
+         * of a stream which has a known size (because we have received a FIN)
+         * but which still has other (previous) stream data yet to be received.
          */
         break;
     }
@@ -658,6 +665,7 @@ int ossl_quic_stream_map_stop_sending_recv_part(QUIC_STREAM_MAP *qsm,
     return ossl_quic_stream_map_schedule_stop_sending(qsm, qs);
 }
 
+/* Called to mark STOP_SENDING for generation, or regeneration after loss. */
 int ossl_quic_stream_map_schedule_stop_sending(QUIC_STREAM_MAP *qsm, QUIC_STREAM *qs)
 {
     if (!qs->stop_sending)
@@ -675,6 +683,14 @@ int ossl_quic_stream_map_schedule_stop_sending(QUIC_STREAM_MAP *qsm, QUIC_STREAM
         return 1; /* ignore */
     case QUIC_RSTREAM_STATE_RECV:
     case QUIC_RSTREAM_STATE_SIZE_KNOWN:
+        /*
+         * RFC 9000 s. 3.5: "An endpoint is expected to send another
+         * STOP_SENDING frame if a packet containing a previous STOP_SENDING is
+         * lost. However, once either all stream data or a RESET_STREAM frame
+         * has been received for the stream -- that is, the stream is in any
+         * state other than "Recv" or "Size Known" -- sending a STOP_SENDING
+         * frame is unnecessary."
+         */
         break;
     }
 
index cb44b0aa693efcf5fa000f101f4d764073dc8d74..42329b42de16c06683285e2d2f2fdd7030b883f0 100644 (file)
@@ -1854,6 +1854,17 @@ static int txp_generate_stream_related(OSSL_QUIC_TX_PACKETISER *txp,
             *have_ack_eliciting = 1;
             tx_helper_unrestrict(h); /* no longer need PING */
             stream->txp_sent_reset_stream = 1;
+
+            /*
+             * The final size of the stream as indicated by RESET_STREAM is used
+             * to ensure a consistent view of flow control state by both
+             * parties; if we happen to send a RESET_STREAM that consumes more
+             * flow control credit, make sure we account for that.
+             */
+            assert(f.final_size <= ossl_quic_txfc_get_swm(&stream->txfc));
+
+            stream->txp_txfc_new_credit_consumed
+                = f.final_size - ossl_quic_txfc_get_swm(&stream->txfc);
         }
 
         /* Stream Flow Control Frames (MAX_STREAM_DATA) */
@@ -1896,6 +1907,8 @@ static int txp_generate_stream_related(OSSL_QUIC_TX_PACKETISER *txp,
             && !ossl_quic_stream_send_is_reset(stream)) {
             int packet_full = 0, stream_drained = 0;
 
+            assert(!stream->want_reset_stream);
+
             if (!txp_generate_stream_frames(txp, h, pn_space, tpkt,
                                             stream->id, stream->sstream,
                                             &stream->txfc,