From 0c04cb98f27ac3393455e366de185b39c66ec37f Mon Sep 17 00:00:00 2001 From: AlmaLinux RelEng Bot Date: Tue, 7 Jul 2026 18:06:11 -0400 Subject: [PATCH] import UBI nginx-1.26.3-6.el10_2.5 --- 0014-Added-max_headers-directive.patch | 298 ++++++++++++++---- ...it-header-length-for-HTTP-2-and-gRPC.patch | 99 ++++++ nginx.spec | 13 +- 3 files changed, 346 insertions(+), 64 deletions(-) create mode 100644 0015-Upstream-limit-header-length-for-HTTP-2-and-gRPC.patch diff --git a/0014-Added-max_headers-directive.patch b/0014-Added-max_headers-directive.patch index 1b75920..5afd520 100644 --- a/0014-Added-max_headers-directive.patch +++ b/0014-Added-max_headers-directive.patch @@ -1,4 +1,4 @@ -From 9bd702431d73abbec44158e123c7ac5b83463fff Mon Sep 17 00:00:00 2001 +From 972780352ac6255796a877300e353484f04a148b Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Fri, 24 May 2024 00:20:01 +0300 Subject: [PATCH] Added max_headers directive. @@ -11,74 +11,220 @@ might be beneficial to better protect backend servers. Requested by Maksim Yevmenkin. +The original patch was modified so that the header count and the limit are +stored in a separate module, ngx_http_header_count_module, instead of the +core module. This allows to avoid changing the size of existing structures +and thus avoid potential ABI breakage and keep all the 3rd party modules +compatible with the new version of nginx without recompilation. + Signed-off-by: Elijah Zupancic Origin: --- - src/http/ngx_http_core_module.c | 10 ++++++++++ - src/http/ngx_http_core_module.h | 2 ++ - src/http/ngx_http_request.c | 9 +++++++++ - src/http/ngx_http_request.h | 1 + - src/http/v2/ngx_http_v2.c | 9 +++++++++ - src/http/v3/ngx_http_v3_request.c | 9 +++++++++ - 6 files changed, 40 insertions(+) + auto/modules | 1 + + src/http/ngx_http_core_module.c | 73 +++++++++++++++++++++++++++++++ + src/http/ngx_http_header_count.hh | 14 ++++++ + src/http/ngx_http_request.c | 24 ++++++++++ + src/http/v2/ngx_http_v2.c | 14 ++++++ + src/http/v3/ngx_http_v3_request.c | 14 ++++++ + 6 files changed, 140 insertions(+) + create mode 100644 src/http/ngx_http_header_count.hh +diff --git a/auto/modules b/auto/modules +index 1a5e421..e7632fd 100644 +--- a/auto/modules ++++ b/auto/modules +@@ -67,6 +67,7 @@ if [ $HTTP = YES ]; then + ngx_module_name="ngx_http_module \ + ngx_http_core_module \ + ngx_http_log_module \ ++ ngx_http_header_count_module \ + ngx_http_upstream_module" + ngx_module_incs="src/http src/http/modules" + ngx_module_deps="src/http/ngx_http.h \ diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c -index 033a3bf..4d11eb3 100644 +index 033a3bf..c736d83 100644 --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c -@@ -252,6 +252,13 @@ static ngx_command_t ngx_http_core_commands[] = { - offsetof(ngx_http_core_srv_conf_t, large_client_header_buffers), - NULL }, +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + + typedef struct { +@@ -39,6 +40,10 @@ static void *ngx_http_core_create_loc_conf(ngx_conf_t *cf); + static char *ngx_http_core_merge_loc_conf(ngx_conf_t *cf, + void *parent, void *child); + ++static void *ngx_http_header_count_create_srv_conf(ngx_conf_t *cf); ++static char *ngx_http_header_count_merge_srv_conf(ngx_conf_t *cf, ++ void *parent, void *child); ++ + static char *ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, + void *dummy); + static char *ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, +@@ -812,6 +817,48 @@ ngx_module_t ngx_http_core_module = { + NGX_MODULE_V1_PADDING + }; + ++static ngx_command_t ngx_http_header_count_commands[] = { ++ + { ngx_string("max_headers"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_SRV_CONF_OFFSET, -+ offsetof(ngx_http_core_srv_conf_t, max_headers), ++ offsetof(ngx_http_header_count_conf_t, max_headers), + NULL }, + - { ngx_string("ignore_invalid_headers"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, - ngx_conf_set_flag_slot, -@@ -3460,6 +3467,7 @@ ngx_http_core_create_srv_conf(ngx_conf_t *cf) - cscf->request_pool_size = NGX_CONF_UNSET_SIZE; - cscf->client_header_timeout = NGX_CONF_UNSET_MSEC; - cscf->client_header_buffer_size = NGX_CONF_UNSET_SIZE; -+ cscf->max_headers = NGX_CONF_UNSET_UINT; - cscf->ignore_invalid_headers = NGX_CONF_UNSET; - cscf->merge_slashes = NGX_CONF_UNSET; - cscf->underscores_in_headers = NGX_CONF_UNSET; -@@ -3501,6 +3509,8 @@ ngx_http_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) - return NGX_CONF_ERROR; - } ++ ngx_null_command ++}; ++ ++static ngx_http_module_t ngx_http_header_count_module_ctx = { ++ NULL, /* preconfiguration */ ++ NULL, /* postconfiguration */ ++ ++ NULL, /* create main configuration */ ++ NULL, /* init main configuration */ ++ ++ ngx_http_header_count_create_srv_conf, /* create server configuration */ ++ ngx_http_header_count_merge_srv_conf, /* merge server configuration */ ++ ++ NULL, /* create location configuration */ ++ NULL /* merge location configuration */ ++}; ++ ++ ++ngx_module_t ngx_http_header_count_module = { ++ NGX_MODULE_V1, ++ &ngx_http_header_count_module_ctx, /* module context */ ++ ngx_http_header_count_commands, /* module directives */ ++ NGX_HTTP_MODULE, /* module type */ ++ NULL, /* init master */ ++ NULL, /* init module */ ++ NULL, /* init process */ ++ NULL, /* init thread */ ++ NULL, /* exit thread */ ++ NULL, /* exit process */ ++ NULL, /* exit master */ ++ NGX_MODULE_V1_PADDING ++}; ++ + ngx_str_t ngx_http_core_get_method = { 3, (u_char *) "GET" }; + +@@ -3653,6 +3700,32 @@ ngx_http_core_create_loc_conf(ngx_conf_t *cf) + } + + ++static void * ++ngx_http_header_count_create_srv_conf(ngx_conf_t *cf) ++{ ++ ngx_http_header_count_conf_t *hccf; ++ ++ hccf = ngx_pcalloc(cf->pool, sizeof(ngx_http_header_count_conf_t)); ++ if (hccf == NULL) { ++ return NULL; ++ } ++ ++ hccf->max_headers = NGX_CONF_UNSET_UINT; ++ return hccf; ++} ++ ++static char * ++ngx_http_header_count_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ++{ ++ ngx_http_header_count_conf_t *prev = parent; ++ ngx_http_header_count_conf_t *conf = child; ++ + ngx_conf_merge_uint_value(conf->max_headers, prev->max_headers, 1000); + - ngx_conf_merge_value(conf->ignore_invalid_headers, - prev->ignore_invalid_headers, 1); - -diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h -index 765e7ff..5af748e 100644 ---- a/src/http/ngx_http_core_module.h -+++ b/src/http/ngx_http_core_module.h -@@ -198,6 +198,8 @@ typedef struct { - - ngx_msec_t client_header_timeout; - -+ ngx_uint_t max_headers; ++ return NGX_CONF_OK; ++} + - ngx_flag_t ignore_invalid_headers; - ngx_flag_t merge_slashes; - ngx_flag_t underscores_in_headers; ++ + static ngx_str_t ngx_http_core_text_html_type = ngx_string("text/html"); + static ngx_str_t ngx_http_core_image_gif_type = ngx_string("image/gif"); + static ngx_str_t ngx_http_core_image_jpeg_type = ngx_string("image/jpeg"); +diff --git a/src/http/ngx_http_header_count.hh b/src/http/ngx_http_header_count.hh +new file mode 100644 +index 0000000..6a8b86d +--- /dev/null ++++ b/src/http/ngx_http_header_count.hh +@@ -0,0 +1,14 @@ ++#ifndef _NGX_HTTP_HEADER_COUNT_H_INCLUDED_ ++#define _NGX_HTTP_HEADER_COUNT_H_INCLUDED_ ++ ++typedef struct { ++ ngx_uint_t count; ++} ngx_http_header_count_t; ++ ++typedef struct { ++ ngx_uint_t max_headers; ++} ngx_http_header_count_conf_t; ++ ++extern ngx_module_t ngx_http_header_count_module; ++ ++#endif /* _NGX_HTTP_HEADER_COUNT_H_INCLUDED_ */ diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c -index 9593b7f..97ed5a3 100644 +index 9593b7f..88165b4 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c -@@ -1489,6 +1489,15 @@ ngx_http_process_request_headers(ngx_event_t *rev) +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + + + static void ngx_http_wait_request_handler(ngx_event_t *ev); +@@ -565,6 +566,7 @@ ngx_http_alloc_request(ngx_connection_t *c) + ngx_http_connection_t *hc; + ngx_http_core_srv_conf_t *cscf; + ngx_http_core_main_conf_t *cmcf; ++ ngx_http_header_count_t *hc_ctx; + + hc = c->data; + +@@ -626,6 +628,13 @@ ngx_http_alloc_request(ngx_connection_t *c) + return NULL; + } + ++ hc_ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_header_count_t)); ++ if (hc_ctx == NULL) { ++ ngx_destroy_pool(r->pool); ++ return NULL; ++ } ++ ngx_http_set_ctx(r, hc_ctx, ngx_http_header_count_module); ++ + #if (NGX_HTTP_SSL) + if (c->ssl && !c->ssl->sendfile) { + r->main_filter_need_in_memory = 1; +@@ -1399,6 +1408,8 @@ ngx_http_process_request_headers(ngx_event_t *rev) + ngx_http_request_t *r; + ngx_http_core_srv_conf_t *cscf; + ngx_http_core_main_conf_t *cmcf; ++ ngx_http_header_count_conf_t *hccf; ++ ngx_http_header_count_t *hc_ctx; + + c = rev->data; + r = c->data; +@@ -1472,6 +1483,10 @@ ngx_http_process_request_headers(ngx_event_t *rev) + rc = ngx_http_parse_header_line(r, r->header_in, + cscf->underscores_in_headers); + ++ ++ hccf = ngx_http_get_module_srv_conf(r, ngx_http_header_count_module); ++ hc_ctx = ngx_http_get_module_ctx(r, ngx_http_header_count_module); ++ + if (rc == NGX_OK) { + + r->request_length += r->header_in->pos - r->header_name_start; +@@ -1489,6 +1504,15 @@ ngx_http_process_request_headers(ngx_event_t *rev) /* a header line has been parsed successfully */ -+ if (r->headers_in.count++ >= cscf->max_headers) { ++ if (hc_ctx->count++ >= hccf->max_headers) { + r->lingering_close = 1; + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent too many header lines"); @@ -90,29 +236,36 @@ index 9593b7f..97ed5a3 100644 h = ngx_list_push(&r->headers_in.headers); if (h == NULL) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); -diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h -index 65c8333..2245280 100644 ---- a/src/http/ngx_http_request.h -+++ b/src/http/ngx_http_request.h -@@ -182,6 +182,7 @@ typedef struct { - - typedef struct { - ngx_list_t headers; -+ ngx_uint_t count; - - ngx_table_elt_t *host; - ngx_table_elt_t *connection; diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c -index 0f5bd3d..a4cce4f 100644 +index 0f5bd3d..b340789 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c -@@ -1817,6 +1817,15 @@ ngx_http_v2_state_process_header(ngx_http_v2_connection_t *h2c, u_char *pos, +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + + + /* errors */ +@@ -1712,6 +1713,8 @@ ngx_http_v2_state_process_header(ngx_http_v2_connection_t *h2c, u_char *pos, + ngx_http_v2_header_t *header; + ngx_http_core_srv_conf_t *cscf; + ngx_http_core_main_conf_t *cmcf; ++ ngx_http_header_count_conf_t *hccf; ++ ngx_http_header_count_t *hc_ctx; + + static ngx_str_t cookie = ngx_string("cookie"); + +@@ -1817,6 +1820,17 @@ ngx_http_v2_state_process_header(ngx_http_v2_connection_t *h2c, u_char *pos, } } else { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); ++ hccf = ngx_http_get_module_srv_conf(r, ngx_http_header_count_module); ++ hc_ctx = ngx_http_get_module_ctx(r, ngx_http_header_count_module); + -+ if (r->headers_in.count++ >= cscf->max_headers) { ++ if (hc_ctx->count++ >= hccf->max_headers) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent too many header lines"); + ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_TOO_LARGE); @@ -123,16 +276,35 @@ index 0f5bd3d..a4cce4f 100644 if (h == NULL) { return ngx_http_v2_connection_error(h2c, diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c -index 0faddd2..75fecbb 100644 +index 0faddd2..a3e10ae 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c -@@ -665,6 +665,15 @@ ngx_http_v3_process_header(ngx_http_request_t *r, ngx_str_t *name, +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + + + static void ngx_http_v3_init_request_stream(ngx_connection_t *c); +@@ -618,6 +619,8 @@ ngx_http_v3_process_header(ngx_http_request_t *r, ngx_str_t *name, + ngx_http_header_t *hh; + ngx_http_core_srv_conf_t *cscf; + ngx_http_core_main_conf_t *cmcf; ++ ngx_http_header_count_conf_t *hccf; ++ ngx_http_header_count_t *hc_ctx; + + static ngx_str_t cookie = ngx_string("cookie"); + +@@ -665,6 +668,17 @@ ngx_http_v3_process_header(ngx_http_request_t *r, ngx_str_t *name, } } else { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); ++ hccf = ngx_http_get_module_srv_conf(r, ngx_http_header_count_module); ++ hc_ctx = ngx_http_get_module_ctx(r, ngx_http_header_count_module); + -+ if (r->headers_in.count++ >= cscf->max_headers) { ++ if (hc_ctx->count++ >= hccf->max_headers) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent too many header lines"); + ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_TOO_LARGE); diff --git a/0015-Upstream-limit-header-length-for-HTTP-2-and-gRPC.patch b/0015-Upstream-limit-header-length-for-HTTP-2-and-gRPC.patch new file mode 100644 index 0000000..16e5158 --- /dev/null +++ b/0015-Upstream-limit-header-length-for-HTTP-2-and-gRPC.patch @@ -0,0 +1,99 @@ +From 83cc0876c6d8f880b7c943e39d922e2286c94a41 Mon Sep 17 00:00:00 2001 +From: Roman Arutyunyan +Date: Tue, 2 Jun 2026 19:37:17 +0400 +Subject: [PATCH] Upstream: limit header length for HTTP/2 and gRPC + +The change applies the HTTP/2 header length limits to avoid buffer +overflow. See 58a7bc3406ac for details. + +Reported by Mufeed VH of Winfunc Research. +--- + src/http/modules/ngx_http_grpc_module.c | 44 +++++++++++++++++++++++++ + 1 file changed, 44 insertions(+) + +diff --git a/src/http/modules/ngx_http_grpc_module.c b/src/http/modules/ngx_http_grpc_module.c +index 9f13089..bb636f3 100644 +--- a/src/http/modules/ngx_http_grpc_module.c ++++ b/src/http/modules/ngx_http_grpc_module.c +@@ -740,6 +740,12 @@ ngx_http_grpc_create_request(ngx_http_request_t *r) + tmp_len = 0; + + } else { ++ if (r->method_name.len > NGX_HTTP_V2_MAX_FIELD) { ++ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ++ "too long http2 method: \"%V\"", &r->method_name); ++ return NGX_ERROR; ++ } ++ + len += 1 + NGX_HTTP_V2_INT_OCTETS + r->method_name.len; + tmp_len = r->method_name.len; + } +@@ -760,6 +766,12 @@ ngx_http_grpc_create_request(ngx_http_request_t *r) + uri_len = r->uri.len + escape + sizeof("?") - 1 + r->args.len; + } + ++ if (uri_len > NGX_HTTP_V2_MAX_FIELD) { ++ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ++ "too long http2 URI"); ++ return NGX_ERROR; ++ } ++ + len += 1 + NGX_HTTP_V2_INT_OCTETS + uri_len; + + if (tmp_len < uri_len) { +@@ -769,6 +781,12 @@ ngx_http_grpc_create_request(ngx_http_request_t *r) + /* :authority header */ + + if (!glcf->host_set) { ++ if (ctx->host.len > NGX_HTTP_V2_MAX_FIELD) { ++ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ++ "too long http2 host: \"%V\"", &ctx->host); ++ return NGX_ERROR; ++ } ++ + len += 1 + NGX_HTTP_V2_INT_OCTETS + ctx->host.len; + + if (tmp_len < ctx->host.len) { +@@ -799,6 +817,18 @@ ngx_http_grpc_create_request(ngx_http_request_t *r) + continue; + } + ++ if (key_len > NGX_HTTP_V2_MAX_FIELD) { ++ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ++ "too long http2 header name"); ++ return NGX_ERROR; ++ } ++ ++ if (val_len > NGX_HTTP_V2_MAX_FIELD) { ++ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ++ "too long http2 header value"); ++ return NGX_ERROR; ++ } ++ + len += 1 + NGX_HTTP_V2_INT_OCTETS + key_len + + NGX_HTTP_V2_INT_OCTETS + val_len; + +@@ -833,6 +863,20 @@ ngx_http_grpc_create_request(ngx_http_request_t *r) + continue; + } + ++ if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) { ++ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ++ "too long http2 header name: \"%V\"", ++ &header[i].key); ++ return NGX_ERROR; ++ } ++ ++ if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) { ++ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ++ "too long http2 header value: \"%V: %V\"", ++ &header[i].key, &header[i].value); ++ return NGX_ERROR; ++ } ++ + len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len + + NGX_HTTP_V2_INT_OCTETS + header[i].value.len; + +-- +2.44.0 + diff --git a/nginx.spec b/nginx.spec index c2369c9..39d53b0 100644 --- a/nginx.spec +++ b/nginx.spec @@ -62,7 +62,7 @@ Name: nginx Epoch: 2 Version: 1.26.3 -Release: 6%{?dist}.4 +Release: 6%{?dist}.5 Summary: A high performance web server and reverse proxy server License: BSD-2-Clause @@ -147,6 +147,10 @@ Patch12: 0013-Rewrite-fix-buffer-overflow-with-overlapping-capture.pat # upstream patch - https://github.com/nginx/nginx/commit/365694160a85229a7cb006738de9260d49ff5fa2 Patch13: 0014-Added-max_headers-directive.patch +# https://redhat.atlassian.net/browse/RHEL-188418 +# upstream patch - https://github.com/nginx/nginx/commit/26d824ec3a2f819300edce0ab3b055751c9843ff.patch +Patch14: 0015-Upstream-limit-header-length-for-HTTP-2-and-gRPC.patch + BuildRequires: make BuildRequires: gcc BuildRequires: gnupg2 @@ -673,6 +677,13 @@ fi %changelog +* Fri Jul 03 2026 Luboš Uhliarik - 2:1.26.3-6.5 +- Resolves: RHEL-191778 - nginx: "HTTP/2 bomb" nginx fix breaks module ABI + causing crashes +- Resolves: RHEL-188402 - nginx: NGINX: Arbitrary code execution or. + Denial of Service via heap-based buffer overflow with crafted HTTP/2 + headers (CVE-2026-42055) + * Mon Jun 08 2026 Luboš Uhliarik - 2:1.26.3-6.4 - Resolves: RHEL-178669 - nginx: code execution and denial of service (CVE-2026-9256)