Mercurial > hg > nginx
comparison src/http/v3/ngx_http_v3_table.c @ 9062:987bee4363d1 quic
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.
author | Roman Arutyunyan <arut@nginx.com> |
---|---|
date | Tue, 03 Jan 2023 16:24:45 +0400 |
parents | efbcdb9b37dc |
children | acb8548c00e9 |
comparison
equal
deleted
inserted
replaced
9061:af5adec171b4 | 9062:987bee4363d1 |
---|---|
11 | 11 |
12 | 12 |
13 #define ngx_http_v3_table_entry_size(n, v) ((n)->len + (v)->len + 32) | 13 #define ngx_http_v3_table_entry_size(n, v) ((n)->len + (v)->len + 32) |
14 | 14 |
15 | 15 |
16 static ngx_int_t ngx_http_v3_evict(ngx_connection_t *c, size_t need); | 16 static ngx_int_t ngx_http_v3_evict(ngx_connection_t *c, size_t target); |
17 static void ngx_http_v3_unblock(void *data); | 17 static void ngx_http_v3_unblock(void *data); |
18 static ngx_int_t ngx_http_v3_new_entry(ngx_connection_t *c); | 18 static ngx_int_t ngx_http_v3_new_entry(ngx_connection_t *c); |
19 | 19 |
20 | 20 |
21 typedef struct { | 21 typedef struct { |
202 ngx_http_v3_session_t *h3c; | 202 ngx_http_v3_session_t *h3c; |
203 ngx_http_v3_dynamic_table_t *dt; | 203 ngx_http_v3_dynamic_table_t *dt; |
204 | 204 |
205 size = ngx_http_v3_table_entry_size(name, value); | 205 size = ngx_http_v3_table_entry_size(name, value); |
206 | 206 |
207 if (ngx_http_v3_evict(c, size) != NGX_OK) { | 207 h3c = ngx_http_v3_get_session(c); |
208 dt = &h3c->table; | |
209 | |
210 if (size > dt->capacity) { | |
211 ngx_log_error(NGX_LOG_ERR, c->log, 0, | |
212 "not enough dynamic table capacity"); | |
208 return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; | 213 return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; |
209 } | 214 } |
210 | |
211 h3c = ngx_http_v3_get_session(c); | |
212 dt = &h3c->table; | |
213 | 215 |
214 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, | 216 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, |
215 "http3 insert [%ui] \"%V\":\"%V\", size:%uz", | 217 "http3 insert [%ui] \"%V\":\"%V\", size:%uz", |
216 dt->base + dt->nelts, name, value, size); | 218 dt->base + dt->nelts, name, value, size); |
217 | 219 |
231 | 233 |
232 dt->elts[dt->nelts++] = field; | 234 dt->elts[dt->nelts++] = field; |
233 dt->size += size; | 235 dt->size += size; |
234 | 236 |
235 dt->insert_count++; | 237 dt->insert_count++; |
238 | |
239 if (ngx_http_v3_evict(c, dt->capacity) != NGX_OK) { | |
240 return NGX_ERROR; | |
241 } | |
236 | 242 |
237 ngx_post_event(&dt->send_insert_count, &ngx_posted_events); | 243 ngx_post_event(&dt->send_insert_count, &ngx_posted_events); |
238 | 244 |
239 if (ngx_http_v3_new_entry(c) != NGX_OK) { | 245 if (ngx_http_v3_new_entry(c) != NGX_OK) { |
240 return NGX_ERROR; | 246 return NGX_ERROR; |
291 ngx_log_error(NGX_LOG_INFO, c->log, 0, | 297 ngx_log_error(NGX_LOG_INFO, c->log, 0, |
292 "client exceeded http3_max_table_capacity limit"); | 298 "client exceeded http3_max_table_capacity limit"); |
293 return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; | 299 return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; |
294 } | 300 } |
295 | 301 |
296 dt = &h3c->table; | 302 if (ngx_http_v3_evict(c, capacity) != NGX_OK) { |
297 | 303 return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; |
298 if (dt->size > capacity) { | 304 } |
299 if (ngx_http_v3_evict(c, dt->size - capacity) != NGX_OK) { | 305 |
300 return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; | 306 dt = &h3c->table; |
301 } | |
302 } | |
303 | |
304 max = capacity / 32; | 307 max = capacity / 32; |
305 prev_max = dt->capacity / 32; | 308 prev_max = dt->capacity / 32; |
306 | 309 |
307 if (max > prev_max) { | 310 if (max > prev_max) { |
308 elts = ngx_alloc(max * sizeof(void *), c->log); | 311 elts = ngx_alloc(max * sizeof(void *), c->log); |
343 ngx_free(dt->elts); | 346 ngx_free(dt->elts); |
344 } | 347 } |
345 | 348 |
346 | 349 |
347 static ngx_int_t | 350 static ngx_int_t |
348 ngx_http_v3_evict(ngx_connection_t *c, size_t need) | 351 ngx_http_v3_evict(ngx_connection_t *c, size_t target) |
349 { | 352 { |
350 size_t size, target; | 353 size_t size; |
351 ngx_uint_t n; | 354 ngx_uint_t n; |
352 ngx_http_v3_field_t *field; | 355 ngx_http_v3_field_t *field; |
353 ngx_http_v3_session_t *h3c; | 356 ngx_http_v3_session_t *h3c; |
354 ngx_http_v3_dynamic_table_t *dt; | 357 ngx_http_v3_dynamic_table_t *dt; |
355 | 358 |
356 h3c = ngx_http_v3_get_session(c); | 359 h3c = ngx_http_v3_get_session(c); |
357 dt = &h3c->table; | 360 dt = &h3c->table; |
358 | |
359 if (need > dt->capacity) { | |
360 ngx_log_error(NGX_LOG_ERR, c->log, 0, | |
361 "not enough dynamic table capacity"); | |
362 return NGX_ERROR; | |
363 } | |
364 | |
365 target = dt->capacity - need; | |
366 n = 0; | 361 n = 0; |
367 | 362 |
368 while (dt->size > target) { | 363 while (dt->size > target) { |
369 field = dt->elts[n++]; | 364 field = dt->elts[n++]; |
370 size = ngx_http_v3_table_entry_size(&field->name, &field->value); | 365 size = ngx_http_v3_table_entry_size(&field->name, &field->value); |