QUIC CHANNEL: Incoming streams implicitly create lower-numbered streams
authorHugo Landau <hlandau@openssl.org>
Tue, 18 Apr 2023 18:30:55 +0000 (19:30 +0100)
committerHugo Landau <hlandau@openssl.org>
Fri, 12 May 2023 13:47:12 +0000 (14:47 +0100)
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/20765)

ssl/quic/quic_channel_local.h
ssl/quic/quic_rx_depack.c

index a1ce833f56052557f5ee4996bcfe9f24e698d08a..f13d0118ee30c1e220835382da15cb019fd4e5c6 100644 (file)
@@ -167,6 +167,15 @@ struct quic_channel_st {
     uint64_t                        next_local_stream_ordinal_bidi;
     uint64_t                        next_local_stream_ordinal_uni;
 
+    /*
+     * Used to track which stream ordinals within a given stream type have been
+     * used by the remote peer. This is an optimisation used to determine
+     * which streams should be implicitly created due to usage of a higher
+     * stream ordinal.
+     */
+    uint64_t                        next_remote_stream_ordinal_bidi;
+    uint64_t                        next_remote_stream_ordinal_uni;
+
     /*
      * Application error code to be used for STOP_SENDING/RESET_STREAM frames
      * used to autoreject incoming streams.
index 7adec6d7b0b22a2af17b0bc32fce374016542c1d..c49a13fe89fab6e2ef34efb603fd62c7593fe180 100644 (file)
@@ -254,7 +254,8 @@ static int depack_do_frame_stream(PACKET *pkt, QUIC_CHANNEL *ch,
 
     stream = ossl_quic_stream_map_get_by_id(&ch->qsm, frame_data.stream_id);
     if (stream == NULL) {
-        uint64_t peer_role, stream_ordinal, *p_next_ordinal;
+        uint64_t peer_role, stream_ordinal;
+        uint64_t *p_next_ordinal_local, *p_next_ordinal_remote;
         int is_uni;
 
         /*
@@ -286,14 +287,33 @@ static int depack_do_frame_stream(PACKET *pkt, QUIC_CHANNEL *ch,
         stream_ordinal = frame_data.stream_id >> 2;
 
         if ((frame_data.stream_id & QUIC_STREAM_INITIATOR_MASK) == peer_role) {
-            /* Peer-created stream which does not yet exist. Create it. */
-            stream = ossl_quic_channel_new_stream_remote(ch, frame_data.stream_id);
-            if (stream == NULL) {
-                ossl_quic_channel_raise_protocol_error(ch,
-                                                       QUIC_ERR_INTERNAL_ERROR,
-                                                       frame_type,
-                                                       "internal error (stream allocation)");
-                return 0;
+            /*
+             * Peer-created stream which does not yet exist. Create it. QUIC
+             * stream ordinals within a given stream type MUST be used in
+             * sequence and receiving a STREAM frame for ordinal n must
+             * implicitly create streams with ordinals [0, n) within that stream
+             * type even if no explicit STREAM frames are received for those
+             * ordinals.
+             */
+            p_next_ordinal_remote = is_uni
+                ? &ch->next_remote_stream_ordinal_uni
+                : &ch->next_remote_stream_ordinal_bidi;
+
+            while (*p_next_ordinal_remote <= stream_ordinal) {
+                uint64_t stream_id = (*p_next_ordinal_remote << 2) |
+                    (frame_data.stream_id
+                     & (QUIC_STREAM_DIR_MASK | QUIC_STREAM_INITIATOR_MASK));
+
+                stream = ossl_quic_channel_new_stream_remote(ch, stream_id);
+                if (stream == NULL) {
+                    ossl_quic_channel_raise_protocol_error(ch,
+                                                           QUIC_ERR_INTERNAL_ERROR,
+                                                           frame_type,
+                                                           "internal error (stream allocation)");
+                    return 0;
+                }
+
+                ++*p_next_ordinal_remote;
             }
 
             /*
@@ -303,11 +323,11 @@ static int depack_do_frame_stream(PACKET *pkt, QUIC_CHANNEL *ch,
         } else {
             /* Locally-created stream which does not yet exist. */
 
-            p_next_ordinal = is_uni
+            p_next_ordinal_local = is_uni
                 ? &ch->next_local_stream_ordinal_uni
                 : &ch->next_local_stream_ordinal_bidi;
 
-            if (stream_ordinal >= *p_next_ordinal) {
+            if (stream_ordinal >= *p_next_ordinal_local) {
                 /*
                  * We never created this stream yet, this is a protocol
                  * violation.