]> git.kaiwu.me - nginx.git/commitdiff
Upstream: limit response header field sizes for HTTP/2 and gRPC
authorZhidao HONG <z.hong@f5.com>
Mon, 8 Jun 2026 03:52:24 +0000 (03:52 +0000)
committerhongzhidao <hongzhidao@gmail.com>
Tue, 14 Jul 2026 12:38:57 +0000 (20:38 +0800)
HTTP/2 and gRPC upstream response header parsing used the HPACK string
length to allocate header name and value buffers.  The length was not
checked against the configured upstream buffer size before allocation.
A malicious upstream could force nginx to allocate excessive
request-pool memory before the header was rejected.

Reject oversized HTTP/2 header name and value lengths before allocation.
Also keep a per-header-block limit based on the upstream buffer size, so
a header block cannot consume unbounded memory across fields.

src/http/modules/ngx_http_grpc_module.c
src/http/modules/ngx_http_proxy_v2_module.c

index 32bdc964cb3e45c94f379b76d81fe4fc9664ba4b..248ec179c0b9c63a6887dae822754eb5f9f60fa1 100644 (file)
@@ -106,6 +106,7 @@ typedef struct {
     ngx_str_t                  value;
 
     u_char                    *field_end;
+    size_t                     header_limit;
     size_t                     field_length;
     size_t                     field_rest;
     u_char                     field_state;
@@ -2702,6 +2703,7 @@ ngx_http_grpc_parse_header(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx,
         if (ctx->type == NGX_HTTP_V2_HEADERS_FRAME) {
             ctx->parsing_headers = 1;
             ctx->fragment_state = 0;
+            ctx->header_limit = r->upstream->conf->buffer_size;
 
             min = (ctx->flags & NGX_HTTP_V2_PADDED_FLAG ? 1 : 0)
                   + (ctx->flags & NGX_HTTP_V2_PRIORITY_FLAG ? 5 : 0);
@@ -2897,7 +2899,7 @@ ngx_http_grpc_parse_fragment(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx,
     ngx_buf_t *b)
 {
     u_char      ch, *p, *last;
-    size_t      size;
+    size_t      len, size;
     ngx_uint_t  index, size_update;
     enum {
         sw_start = 0,
@@ -3236,6 +3238,14 @@ ngx_http_grpc_parse_fragment(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx,
             ctx->name.len = ctx->field_huffman ?
                             ctx->field_length * 8 / 5 : ctx->field_length;
 
+            if (ctx->name.len > ctx->header_limit) {
+                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                              "upstream sent too large http2 "
+                              "header name length: %uz",
+                              ctx->name.len);
+                return NGX_ERROR;
+            }
+
             ctx->name.data = ngx_pnalloc(r->pool, ctx->name.len + 1);
             if (ctx->name.data == NULL) {
                 return NGX_ERROR;
@@ -3345,6 +3355,14 @@ ngx_http_grpc_parse_fragment(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx,
             ctx->value.len = ctx->field_huffman ?
                              ctx->field_length * 8 / 5 : ctx->field_length;
 
+            if (ctx->value.len > ctx->header_limit) {
+                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                              "upstream sent too large http2 "
+                              "header value length: %uz",
+                              ctx->value.len);
+                return NGX_ERROR;
+            }
+
             ctx->value.data = ngx_pnalloc(r->pool, ctx->value.len + 1);
             if (ctx->value.data == NULL) {
                 return NGX_ERROR;
@@ -3434,6 +3452,16 @@ ngx_http_grpc_parse_fragment(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx,
             }
         }
 
+        len = ctx->name.len + ctx->value.len;
+
+        if (len > ctx->header_limit) {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "upstream sent too large http2 header");
+            return NGX_ERROR;
+        }
+
+        ctx->header_limit -= len;
+
         return NGX_OK;
     }
 
index 010c02a8645b6f82da3ac741b4937d6ac9c3165e..2a1ff74fbfa79ac7cf70aac4db49014e101cfc5d 100644 (file)
@@ -77,6 +77,7 @@ typedef struct {
     ngx_str_t                      value;
 
     u_char                        *field_end;
+    size_t                         header_limit;
     size_t                         field_length;
     size_t                         field_rest;
     u_char                         field_state;
@@ -2535,6 +2536,7 @@ ngx_http_proxy_v2_parse_header(ngx_http_request_t *r,
         if (ctx->type == NGX_HTTP_V2_HEADERS_FRAME) {
             ctx->parsing_headers = 1;
             ctx->fragment_state = 0;
+            ctx->header_limit = r->upstream->conf->buffer_size;
 
             min = (ctx->flags & NGX_HTTP_V2_PADDED_FLAG ? 1 : 0)
                   + (ctx->flags & NGX_HTTP_V2_PRIORITY_FLAG ? 5 : 0);
@@ -2730,7 +2732,7 @@ ngx_http_proxy_v2_parse_fragment(ngx_http_request_t *r,
     ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b)
 {
     u_char      ch, *p, *last;
-    size_t      size;
+    size_t      len, size;
     ngx_uint_t  index, size_update;
     enum {
         sw_start = 0,
@@ -3070,6 +3072,14 @@ ngx_http_proxy_v2_parse_fragment(ngx_http_request_t *r,
             ctx->name.len = ctx->field_huffman ?
                             ctx->field_length * 8 / 5 : ctx->field_length;
 
+            if (ctx->name.len > ctx->header_limit) {
+                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                              "upstream sent too large http2 "
+                              "header name length: %uz",
+                              ctx->name.len);
+                return NGX_ERROR;
+            }
+
             ctx->name.data = ngx_pnalloc(r->pool, ctx->name.len + 1);
             if (ctx->name.data == NULL) {
                 return NGX_ERROR;
@@ -3179,6 +3189,14 @@ ngx_http_proxy_v2_parse_fragment(ngx_http_request_t *r,
             ctx->value.len = ctx->field_huffman ?
                              ctx->field_length * 8 / 5 : ctx->field_length;
 
+            if (ctx->value.len > ctx->header_limit) {
+                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                              "upstream sent too large http2 "
+                              "header value length: %uz",
+                              ctx->value.len);
+                return NGX_ERROR;
+            }
+
             ctx->value.data = ngx_pnalloc(r->pool, ctx->value.len + 1);
             if (ctx->value.data == NULL) {
                 return NGX_ERROR;
@@ -3272,6 +3290,16 @@ ngx_http_proxy_v2_parse_fragment(ngx_http_request_t *r,
             }
         }
 
+        len = ctx->name.len + ctx->value.len;
+
+        if (len > ctx->header_limit) {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "upstream sent too large http2 header");
+            return NGX_ERROR;
+        }
+
+        ctx->header_limit -= len;
+
         return NGX_OK;
     }