[nginx] HTTP/3: decoder stream pre-creation.

Sergey Kandaurov pluknet at nginx.com
Wed May 29 14:58:18 UTC 2024


details:   https://hg.nginx.org/nginx/rev/08f8e9c33a08
branches:  stable-1.26
changeset: 9262:08f8e9c33a08
user:      Roman Arutyunyan <arut at nginx.com>
date:      Tue May 28 17:18:28 2024 +0400
description:
HTTP/3: decoder stream pre-creation.

Previously a decoder stream was created on demand for sending Section
Acknowledgement, Stream Cancellation and Insert Count Increment.  If conditions
for sending any of these instructions never happen, a decoder stream is not
created at all.  These conditions include client not using the dynamic table and
no streams abandoned by server (RFC 9204, Section 2.2.2.2).  However RFC 9204,
Section 4.2 defines only one condition for not creating a decoder stream:

   An endpoint MAY avoid creating a decoder stream if its decoder sets
   the maximum capacity of the dynamic table to zero.

The change enables pre-creation of the decoder stream at HTTP/3 session
initialization if maximum dynamic table capacity is not zero.  Note that this
value is currently hardcoded to 4096 bytes and is not configurable, so the
stream is now always created.

Also, the change fixes a potential stack overflow when creating a decoder
stream in ngx_http_v3_send_cancel_stream() while draining a request stream by
ngx_drain_connections().  Creating a decoder stream involves calling
ngx_get_connection(), which calls ngx_drain_connections(), which will drain the
same request stream again.  If client's MAX_STREAMS for uni stream is high
enough, these recursive calls will continue until we run out of stack.
Otherwise, decoder stream creation will fail at some point and the request
stream connection will be drained.  This may result in use-after-free, since
this connection could still be referenced up the stack.

diffstat:

 src/http/v3/ngx_http_v3_request.c |  20 ++++++++++++++------
 src/http/v3/ngx_http_v3_uni.c     |   4 +---
 src/http/v3/ngx_http_v3_uni.h     |   2 ++
 3 files changed, 17 insertions(+), 9 deletions(-)

diffs (73 lines):

diff -r 04bc350b2919 -r 08f8e9c33a08 src/http/v3/ngx_http_v3_request.c
--- a/src/http/v3/ngx_http_v3_request.c	Tue May 28 17:17:19 2024 +0400
+++ b/src/http/v3/ngx_http_v3_request.c	Tue May 28 17:18:28 2024 +0400
@@ -134,7 +134,17 @@ ngx_http_v3_init(ngx_connection_t *c)
         }
     }
 
-    return ngx_http_v3_send_settings(c);
+    if (ngx_http_v3_send_settings(c) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    if (h3scf->max_table_capacity > 0) {
+        if (ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER) == NULL) {
+            return NGX_ERROR;
+        }
+    }
+
+    return NGX_OK;
 }
 
 
@@ -398,14 +408,12 @@ ngx_http_v3_wait_request_handler(ngx_eve
 void
 ngx_http_v3_reset_stream(ngx_connection_t *c)
 {
-    ngx_http_v3_session_t   *h3c;
-    ngx_http_v3_srv_conf_t  *h3scf;
-
-    h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module);
+    ngx_http_v3_session_t  *h3c;
 
     h3c = ngx_http_v3_get_session(c);
 
-    if (h3scf->max_table_capacity > 0 && !c->read->eof && !h3c->hq
+    if (!c->read->eof && !h3c->hq
+        && h3c->known_streams[NGX_HTTP_V3_STREAM_SERVER_DECODER]
         && (c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0)
     {
         (void) ngx_http_v3_send_cancel_stream(c, c->quic->id);
diff -r 04bc350b2919 -r 08f8e9c33a08 src/http/v3/ngx_http_v3_uni.c
--- a/src/http/v3/ngx_http_v3_uni.c	Tue May 28 17:17:19 2024 +0400
+++ b/src/http/v3/ngx_http_v3_uni.c	Tue May 28 17:18:28 2024 +0400
@@ -20,8 +20,6 @@ static void ngx_http_v3_close_uni_stream
 static void ngx_http_v3_uni_read_handler(ngx_event_t *rev);
 static void ngx_http_v3_uni_dummy_read_handler(ngx_event_t *wev);
 static void ngx_http_v3_uni_dummy_write_handler(ngx_event_t *wev);
-static ngx_connection_t *ngx_http_v3_get_uni_stream(ngx_connection_t *c,
-    ngx_uint_t type);
 
 
 void
@@ -307,7 +305,7 @@ ngx_http_v3_uni_dummy_write_handler(ngx_
 }
 
 
-static ngx_connection_t *
+ngx_connection_t *
 ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type)
 {
     u_char                     buf[NGX_HTTP_V3_VARLEN_INT_LEN];
diff -r 04bc350b2919 -r 08f8e9c33a08 src/http/v3/ngx_http_v3_uni.h
--- a/src/http/v3/ngx_http_v3_uni.h	Tue May 28 17:17:19 2024 +0400
+++ b/src/http/v3/ngx_http_v3_uni.h	Tue May 28 17:18:28 2024 +0400
@@ -19,6 +19,8 @@ ngx_int_t ngx_http_v3_register_uni_strea
 
 ngx_int_t ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id);
 
+ngx_connection_t *ngx_http_v3_get_uni_stream(ngx_connection_t *c,
+    ngx_uint_t type);
 ngx_int_t ngx_http_v3_send_settings(ngx_connection_t *c);
 ngx_int_t ngx_http_v3_send_goaway(ngx_connection_t *c, uint64_t id);
 ngx_int_t ngx_http_v3_send_ack_section(ngx_connection_t *c,


More information about the nginx-devel mailing list