diff options
author | Roman Arutyunyan <arut@nginx.com> | 2024-05-28 17:18:28 +0400 |
---|---|---|
committer | Roman Arutyunyan <arut@nginx.com> | 2024-05-28 17:18:28 +0400 |
commit | 0fd59c8b565c4577f7c25b9e6450bd311d18f5e2 (patch) | |
tree | b2f5a6c122984bcc0ccac3b6cd41944da09da6c0 /src/http/v3/ngx_http_v3_request.c | |
parent | 683e304e8bfe881ef983a0f9ef5e724eec2bd974 (diff) | |
download | nginx-0fd59c8b565c4577f7c25b9e6450bd311d18f5e2.tar.gz nginx-0fd59c8b565c4577f7c25b9e6450bd311d18f5e2.zip |
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 (limited to 'src/http/v3/ngx_http_v3_request.c')
-rw-r--r-- | src/http/v3/ngx_http_v3_request.c | 20 |
1 files changed, 14 insertions, 6 deletions
diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index aec122be6..e41ad50a8 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -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_event_t *rev) 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); |