QUIC RXDP/QSM: Enforce MAX_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)
Also use accept queue popping by the application as the retirement
event, i.e., as the cue to increase the limit.

Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/20765)

include/internal/quic_stream_map.h
ssl/quic/quic_impl.c
ssl/quic/quic_rx_depack.c
ssl/quic/quic_stream_map.c
test/quic_txp_test.c

index 78ec703fbc9420b6e6ce9a2f016d9e01b9f8f547..81312e8b4ad9a8238d62ef79d4feae0eaec1cfb6 100644 (file)
@@ -138,6 +138,8 @@ typedef struct quic_stream_map_st {
     QUIC_STREAM             *rr_cur;
     uint64_t                (*get_stream_limit_cb)(int uni, void *arg);
     void                    *get_stream_limit_cb_arg;
+    QUIC_RXFC               *max_streams_bidi_rxfc;
+    QUIC_RXFC               *max_streams_uni_rxfc;
 } QUIC_STREAM_MAP;
 
 /*
@@ -155,7 +157,9 @@ typedef struct quic_stream_map_st {
  */
 int ossl_quic_stream_map_init(QUIC_STREAM_MAP *qsm,
                               uint64_t (*get_stream_limit_cb)(int uni, void *arg),
-                              void *get_stream_limit_cb_arg);
+                              void *get_stream_limit_cb_arg,
+                              QUIC_RXFC *max_streams_bidi_rxfc,
+                              QUIC_RXFC *max_streams_uni_rxfc);
 
 /*
  * Any streams still in the map will be released as though
@@ -246,12 +250,14 @@ void ossl_quic_stream_map_push_accept_queue(QUIC_STREAM_MAP *qsm,
 QUIC_STREAM *ossl_quic_stream_map_peek_accept_queue(QUIC_STREAM_MAP *qsm);
 
 /*
- * Removes a stream from the accept queue.
+ * Removes a stream from the accept queue. rtt is the estimated connection RTT.
+ * The stream is retired for the purposes of MAX_STREAMS RXFC.
  *
  * Precondition: s is in the accept queue.
  */
 void ossl_quic_stream_map_remove_from_accept_queue(QUIC_STREAM_MAP *qsm,
-                                                   QUIC_STREAM *s);
+                                                   QUIC_STREAM *s,
+                                                   OSSL_TIME rtt);
 
 /* Returns the length of the accept queue. */
 size_t ossl_quic_stream_map_get_accept_queue_len(QUIC_STREAM_MAP *qsm);
index 98f96a770342b4edb7c6428da66adaba5752c242..e3402e5c651f1fd2cc82d2d61a46985cdcfa19c5 100644 (file)
@@ -2231,6 +2231,7 @@ SSL *ossl_quic_accept_stream(SSL *s, uint64_t flags)
     QUIC_STREAM_MAP *qsm;
     QUIC_STREAM *qs;
     QUIC_XSO *xso;
+    OSSL_RTT_INFO rtt_info;
 
     if (!expect_quic_conn_only(s, &ctx))
         return NULL;
@@ -2270,7 +2271,9 @@ SSL *ossl_quic_accept_stream(SSL *s, uint64_t flags)
     if (xso == NULL)
         goto out;
 
-    ossl_quic_stream_map_remove_from_accept_queue(qsm, qs);
+    ossl_statm_get_rtt_info(ossl_quic_channel_get_statm(ctx.qc->ch), &rtt_info);
+    ossl_quic_stream_map_remove_from_accept_queue(qsm, qs,
+                                                  rtt_info.smoothed_rtt);
     new_s = &xso->ssl;
 
     /* Calling this function inhibits default XSO autocreation. */
index c49a13fe89fab6e2ef34efb603fd62c7593fe180..a81f47d6d924128bd612e11f0a6b4ce0235be0a1 100644 (file)
@@ -256,6 +256,7 @@ static int depack_do_frame_stream(PACKET *pkt, QUIC_CHANNEL *ch,
     if (stream == NULL) {
         uint64_t peer_role, stream_ordinal;
         uint64_t *p_next_ordinal_local, *p_next_ordinal_remote;
+        QUIC_RXFC *max_streams_fc;
         int is_uni;
 
         /*
@@ -299,6 +300,32 @@ static int depack_do_frame_stream(PACKET *pkt, QUIC_CHANNEL *ch,
                 ? &ch->next_remote_stream_ordinal_uni
                 : &ch->next_remote_stream_ordinal_bidi;
 
+            /* Check this isn't violating stream count flow control. */
+            max_streams_fc = is_uni
+                ? &ch->max_streams_uni_rxfc
+                : &ch->max_streams_bidi_rxfc;
+
+            if (!ossl_quic_rxfc_on_rx_stream_frame(max_streams_fc,
+                                                   stream_ordinal + 1,
+                                                   /*is_fin=*/0)) {
+                ossl_quic_channel_raise_protocol_error(ch,
+                                                       QUIC_ERR_INTERNAL_ERROR,
+                                                       frame_type,
+                                                       "internal error (stream count RXFC)");
+                return 0;
+            }
+
+            if (ossl_quic_rxfc_get_error(max_streams_fc, 0) != QUIC_ERR_NO_ERROR) {
+                ossl_quic_channel_raise_protocol_error(ch, QUIC_ERR_STREAM_LIMIT_ERROR,
+                                                       frame_type,
+                                                       "exceeded maximum allowed streams");
+                return 0;
+            }
+
+            /*
+             * Create the named stream and any streams coming before it yet to
+             * be created.
+             */
             while (*p_next_ordinal_remote <= stream_ordinal) {
                 uint64_t stream_id = (*p_next_ordinal_remote << 2) |
                     (frame_data.stream_id
index 06bf1cfa349227c24c902c7ec067c3f0c3aabf2e..a9c616ea9f2d446980edb3202b69911be8cf590b 100644 (file)
@@ -102,7 +102,9 @@ static int cmp_stream(const QUIC_STREAM *a, const QUIC_STREAM *b)
 
 int ossl_quic_stream_map_init(QUIC_STREAM_MAP *qsm,
                               uint64_t (*get_stream_limit_cb)(int uni, void *arg),
-                              void *get_stream_limit_cb_arg)
+                              void *get_stream_limit_cb_arg,
+                              QUIC_RXFC *max_streams_bidi_rxfc,
+                              QUIC_RXFC *max_streams_uni_rxfc)
 {
     qsm->map = lh_QUIC_STREAM_new(hash_stream, cmp_stream);
     qsm->active_list.prev = qsm->active_list.next = &qsm->active_list;
@@ -114,6 +116,8 @@ int ossl_quic_stream_map_init(QUIC_STREAM_MAP *qsm,
 
     qsm->get_stream_limit_cb        = get_stream_limit_cb;
     qsm->get_stream_limit_cb_arg    = get_stream_limit_cb_arg;
+    qsm->max_streams_bidi_rxfc      = max_streams_bidi_rxfc;
+    qsm->max_streams_uni_rxfc       = max_streams_uni_rxfc;
     return 1;
 }
 
@@ -294,11 +298,24 @@ void ossl_quic_stream_map_push_accept_queue(QUIC_STREAM_MAP *qsm,
     ++qsm->num_accept;
 }
 
+static QUIC_RXFC *qsm_get_max_streams_rxfc(QUIC_STREAM_MAP *qsm, QUIC_STREAM *s)
+{
+    return ossl_quic_stream_is_bidi(s)
+        ? qsm->max_streams_bidi_rxfc
+        : qsm->max_streams_uni_rxfc;
+}
+
 void ossl_quic_stream_map_remove_from_accept_queue(QUIC_STREAM_MAP *qsm,
-                                                   QUIC_STREAM *s)
+                                                   QUIC_STREAM *s,
+                                                   OSSL_TIME rtt)
 {
+    QUIC_RXFC *max_streams_rxfc;
+
     list_remove(&qsm->accept_list, &s->accept_node);
     --qsm->num_accept;
+
+    if ((max_streams_rxfc = qsm_get_max_streams_rxfc(qsm, s)) != NULL)
+        ossl_quic_rxfc_on_retire(max_streams_rxfc, 1, rtt);
 }
 
 size_t ossl_quic_stream_map_get_accept_queue_len(QUIC_STREAM_MAP *qsm)
index dd85bb5692226d9a8e4e81ea25b834188934a910..dca8c71398213ebac756b54cdb24203ce3e07a6b 100644 (file)
@@ -174,7 +174,9 @@ static int helper_init(struct helper *h)
                                                h->cc_data)))
         goto err;
 
-    if (!TEST_true(ossl_quic_stream_map_init(&h->qsm, NULL, NULL)))
+    if (!TEST_true(ossl_quic_stream_map_init(&h->qsm, NULL, NULL,
+                                             &h->max_streams_bidi_rxfc,
+                                             &h->max_streams_uni_rxfc)))
         goto err;
 
     h->have_qsm = 1;