diff options
author | Roman Arutyunyan <arut@nginx.com> | 2023-01-03 16:24:45 +0400 |
---|---|---|
committer | Roman Arutyunyan <arut@nginx.com> | 2023-01-03 16:24:45 +0400 |
commit | d04f45ac5bddb034ec8c5b0874a7358a991d1b77 (patch) | |
tree | 54f91b662850337657f5109bc70b273f8e04cf25 /src | |
parent | f5aa66bd309a4cbec67285770c77db5d33347c27 (diff) | |
download | nginx-d04f45ac5bddb034ec8c5b0874a7358a991d1b77.tar.gz nginx-d04f45ac5bddb034ec8c5b0874a7358a991d1b77.zip |
HTTP/3: handled insertion reference to a going to be evicted entry.
As per RFC 9204, section 3.2.2, a new entry can reference an entry in the
dynamic table that will be evicted when adding this new entry into the dynamic
table.
Previously, such inserts resulted in use-after-free since the old entry was
evicted before the insertion (ticket #2431). Now it's evicted after the
insertion.
This change fixes Insert with Name Reference and Duplicate encoder instructions.
Diffstat (limited to 'src')
-rw-r--r-- | src/http/v3/ngx_http_v3_table.c | 37 |
1 files changed, 16 insertions, 21 deletions
diff --git a/src/http/v3/ngx_http_v3_table.c b/src/http/v3/ngx_http_v3_table.c index 7e07a15b9..f49a8fc5e 100644 --- a/src/http/v3/ngx_http_v3_table.c +++ b/src/http/v3/ngx_http_v3_table.c @@ -13,7 +13,7 @@ #define ngx_http_v3_table_entry_size(n, v) ((n)->len + (v)->len + 32) -static ngx_int_t ngx_http_v3_evict(ngx_connection_t *c, size_t need); +static ngx_int_t ngx_http_v3_evict(ngx_connection_t *c, size_t target); static void ngx_http_v3_unblock(void *data); static ngx_int_t ngx_http_v3_new_entry(ngx_connection_t *c); @@ -204,13 +204,15 @@ ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value) size = ngx_http_v3_table_entry_size(name, value); - if (ngx_http_v3_evict(c, size) != NGX_OK) { - return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; - } - h3c = ngx_http_v3_get_session(c); dt = &h3c->table; + if (size > dt->capacity) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "not enough dynamic table capacity"); + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; + } + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 insert [%ui] \"%V\":\"%V\", size:%uz", dt->base + dt->nelts, name, value, size); @@ -234,6 +236,10 @@ ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value) dt->insert_count++; + if (ngx_http_v3_evict(c, dt->capacity) != NGX_OK) { + return NGX_ERROR; + } + ngx_post_event(&dt->send_insert_count, &ngx_posted_events); if (ngx_http_v3_new_entry(c) != NGX_OK) { @@ -293,14 +299,11 @@ ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; } - dt = &h3c->table; - - if (dt->size > capacity) { - if (ngx_http_v3_evict(c, dt->size - capacity) != NGX_OK) { - return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; - } + if (ngx_http_v3_evict(c, capacity) != NGX_OK) { + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; } + dt = &h3c->table; max = capacity / 32; prev_max = dt->capacity / 32; @@ -345,9 +348,9 @@ ngx_http_v3_cleanup_table(ngx_http_v3_session_t *h3c) static ngx_int_t -ngx_http_v3_evict(ngx_connection_t *c, size_t need) +ngx_http_v3_evict(ngx_connection_t *c, size_t target) { - size_t size, target; + size_t size; ngx_uint_t n; ngx_http_v3_field_t *field; ngx_http_v3_session_t *h3c; @@ -355,14 +358,6 @@ ngx_http_v3_evict(ngx_connection_t *c, size_t need) h3c = ngx_http_v3_get_session(c); dt = &h3c->table; - - if (need > dt->capacity) { - ngx_log_error(NGX_LOG_ERR, c->log, 0, - "not enough dynamic table capacity"); - return NGX_ERROR; - } - - target = dt->capacity - need; n = 0; while (dt->size > target) { |