]> git.kaiwu.me - nginx.git/commitdiff
Proxy: fix large body with proxy_set_body and HTTP/2
authorRoman Arutyunyan <arut@nginx.com>
Mon, 4 May 2026 14:43:17 +0000 (18:43 +0400)
committerSergey Kandaurov <s.kandaurov@f5.com>
Wed, 13 May 2026 17:20:55 +0000 (21:20 +0400)
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.

src/http/modules/ngx_http_proxy_v2_module.c

index 7cff58d3b4ccfaa310cf909816cd5a713c967814..0be5691aa16b9b7d34fd76a32044f867d171e68d 100644 (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,14 +872,43 @@ ngx_http_proxy_v2_create_request(ngx_http_request_t *r)
 
         b->last_buf = 1;
 
-    } else {
+    } else if (body_len) {
+
         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;
+        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;
+
+        f = (ngx_http_proxy_v2_frame_t *) headers_frame;
+        f->flags |= NGX_HTTP_V2_END_STREAM_FLAG;
+
         b->last_buf = 1;
     }