Proxy: fix large body with proxy_set_body and HTTP/2

Previously, if proxy_set_body was used with HTTP/2, and body size exceeded
16M, then an overflow happened in the 24-bit DATA frame size, which resulted
in sending unframed bytes, potentially allowing for an injection.

Also, DATA frame size could exceed NGX_HTTP_V2_DEFAULT_FRAME_SIZE (16K) and
available send window.

Reported by Mufeed VH of Winfunc Research.
This commit is contained in:
Roman Arutyunyan 2026-05-04 18:43:17 +04:00 committed by Sergey Kandaurov
parent 524977e7c5
commit a0e742944d

View file

@ -458,9 +458,6 @@ ngx_http_proxy_v2_create_request(ngx_http_request_t *r)
ctx->ctx.internal_body_length = body_len;
len += sizeof(ngx_http_proxy_v2_frame_t);
len += body_len;
} else if (r->headers_in.chunked && r->reading_body) {
ctx->ctx.internal_body_length = -1;
@ -834,34 +831,6 @@ ngx_http_proxy_v2_create_request(ngx_http_request_t *r)
f->flags |= NGX_HTTP_V2_END_HEADERS_FLAG;
if (plcf->body_values) {
f = (ngx_http_proxy_v2_frame_t *) b->last;
b->last += sizeof(ngx_http_proxy_v2_frame_t);
f->length_0 = (u_char) ((body_len >> 16) & 0xff);
f->length_1 = (u_char) ((body_len >> 8) & 0xff);
f->length_2 = (u_char) (body_len & 0xff);
f->type = NGX_HTTP_V2_DATA_FRAME;
f->flags = NGX_HTTP_V2_END_STREAM_FLAG;
f->stream_id_0 = 0;
f->stream_id_1 = 0;
f->stream_id_2 = 0;
f->stream_id_3 = 1;
e.ip = plcf->body_values->elts;
e.pos = b->last;
e.request = r;
e.flushed = 1;
e.skip = 0;
while (*(uintptr_t *) e.ip) {
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
}
b->last = e.pos;
}
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header: %*xs%s, len: %uz",
(size_t) ngx_min(b->last - b->pos, 256), b->pos,
@ -903,13 +872,42 @@ ngx_http_proxy_v2_create_request(ngx_http_request_t *r)
b->last_buf = 1;
} else if (body_len) {
u->request_bufs = cl;
b = ngx_create_temp_buf(r->pool, body_len);
if (b == NULL) {
return NGX_ERROR;
}
cl->next = ngx_alloc_chain_link(r->pool);
if (cl->next == NULL) {
return NGX_ERROR;
}
cl = cl->next;
cl->buf = b;
e.ip = plcf->body_values->elts;
e.pos = b->last;
e.request = r;
e.flushed = 1;
e.skip = 0;
while (*(uintptr_t *) e.ip) {
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
}
b->last = e.pos;
b->last_buf = 1;
} else {
u->request_bufs = cl;
if (plcf->body_values == NULL) {
f = (ngx_http_proxy_v2_frame_t *) headers_frame;
f->flags |= NGX_HTTP_V2_END_STREAM_FLAG;
}
f = (ngx_http_proxy_v2_frame_t *) headers_frame;
f->flags |= NGX_HTTP_V2_END_STREAM_FLAG;
b->last_buf = 1;
}