# HG changeset patch # User Roman Arutyunyan # Date 1672748685 -14400 # Node ID 987bee4363d10895f4bd1a40fc4347c49763e90f # Parent af5adec171b4e86397abf4a103a2de6c5ee82b21 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. diff --git a/src/http/v3/ngx_http_v3_table.c b/src/http/v3/ngx_http_v3_table.c --- 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, size = ngx_http_v3_table_entry_size(name, value); - if (ngx_http_v3_evict(c, size) != NGX_OK) { + 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; } - h3c = ngx_http_v3_get_session(c); - dt = &h3c->table; - 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, 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_ 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_se 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, s 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) {