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.
This commit is contained in:
Roman Arutyunyan 2023-01-03 16:24:45 +04:00
parent 8d5850da1f
commit bc654726e1
1 changed files with 16 additions and 21 deletions

View File

@ -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) {