]> git.kaiwu.me - nginx.git/commitdiff
HTTP/3: avoid recreation of standard client uni streams
authorRoman Arutyunyan <arut@nginx.com>
Tue, 19 May 2026 12:09:35 +0000 (16:09 +0400)
committerRoman Arutyunyan <arutyunyan.roman@gmail.com>
Wed, 17 Jun 2026 14:40:35 +0000 (07:40 -0700)
Creating a control/encoder/decoder stream while another such stream
already exists, is not allowed.  Also, closing such a stream results
in connection closure with NGX_HTTP_V3_ERR_CLOSED_CRITICAL_STREAM.
However, since stream creation and connection closure are asynchronous,
there could be a window where two control/encoder/decoder streams
could coexist within a single cycle iteration.  This could result
in reusing parsing context, such as encoder insert buffer.

The change adds a mask for all standard client uni streams ever created.
This allows to check if a stream of this type was created before.
While here, mandatory stream validation now also uses this mask.

src/http/v3/ngx_http_v3.h
src/http/v3/ngx_http_v3_uni.c

index 8fd212c1f085d469e58ad0a9f1f3962b010b4d3f..af6b362a716246fd018c0a90b76e9a39a4e8f775 100644 (file)
@@ -138,6 +138,7 @@ struct ngx_http_v3_session_s {
 
     unsigned                      goaway:1;
     unsigned                      hq:1;
+    unsigned                      created_streams:NGX_HTTP_V3_MAX_KNOWN_STREAM;
 
     ngx_connection_t             *known_streams[NGX_HTTP_V3_MAX_KNOWN_STREAM];
 };
index 302064b8b2bd6faf0f8b85ca0149b6304e745a25..5d183aa9c170c7ec615ebf9960270928ad651f74 100644 (file)
@@ -105,6 +105,7 @@ ngx_int_t
 ngx_http_v3_register_uni_stream(ngx_connection_t *c, uint64_t type)
 {
     ngx_int_t                  index;
+    ngx_uint_t                 streams;
     ngx_http_v3_session_t     *h3c;
     ngx_http_v3_uni_stream_t  *us;
 
@@ -139,10 +140,11 @@ ngx_http_v3_register_uni_stream(ngx_connection_t *c, uint64_t type)
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                        "http3 stream 0x%02xL", type);
 
-        if (h3c->known_streams[NGX_HTTP_V3_STREAM_CLIENT_ENCODER] == NULL
-            || h3c->known_streams[NGX_HTTP_V3_STREAM_CLIENT_DECODER] == NULL
-            || h3c->known_streams[NGX_HTTP_V3_STREAM_CLIENT_CONTROL] == NULL)
-        {
+        streams = (1 << NGX_HTTP_V3_STREAM_CLIENT_ENCODER)
+                  | (1 << NGX_HTTP_V3_STREAM_CLIENT_DECODER)
+                  | (1 << NGX_HTTP_V3_STREAM_CLIENT_CONTROL);
+
+        if ((h3c->created_streams & streams) != streams) {
             ngx_log_error(NGX_LOG_INFO, c->log, 0, "missing mandatory stream");
             return NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR;
         }
@@ -151,12 +153,13 @@ ngx_http_v3_register_uni_stream(ngx_connection_t *c, uint64_t type)
     }
 
     if (index >= 0) {
-        if (h3c->known_streams[index]) {
-            ngx_log_error(NGX_LOG_INFO, c->log, 0, "stream exists");
+        if (h3c->created_streams & (1 << index)) {
+            ngx_log_error(NGX_LOG_INFO, c->log, 0, "stream already created");
             return NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR;
         }
 
         h3c->known_streams[index] = c;
+        h3c->created_streams |= 1 << index;
 
         us = c->data;
         us->index = index;