Fixes CVE-2026-9256, CVE-2026-42055 and HTTP/2: Remote Denial of Service via

compression bomb and Slowloris-style attack

Resolves: RHEL-178674 - nginx: code execution and denial of
service (CVE-2026-9256)

Resolves: RHEL-182545 - nginx: HTTP/2: Remote Denial of Service via
compression bomb and Slowloris-style attack

Resolves: RHEL-188404 - nginx: NGINX: Arbitrary code execution or.
Denial of Service via heap-based buffer overflow with crafted HTTP/2
headers (CVE-2026-42055)
This commit is contained in:
Luboš Uhliarik 2026-07-20 10:11:08 +02:00
parent 5956d5e231
commit 7dab091a95
4 changed files with 502 additions and 0 deletions

View File

@ -0,0 +1,72 @@
From d2e284542086632811980b08670852102226acae Mon Sep 17 00:00:00 2001
From: Roman Arutyunyan <arut@nginx.com>
Date: Thu, 14 May 2026 18:42:18 +0400
Subject: [PATCH] Rewrite: fix buffer overflow with overlapping captures
When the rewrite replacement string had no variables, but had
overlapping captures, the length of the allocated buffer could be
smaller than the replacement string. This could happen either
when the "redirect" parameter is specified, or when arguments are
present in the replacement string.
The following configurations resulted in heap buffer overflow when
using URI "/++++++++++++++++++++++++++++++":
location / {
rewrite ^/((.*))$ http://127.0.0.1:8080/$1$2 redirect;
return 200 foo;
}
location / {
rewrite ^/((.*))$ http://127.0.0.1:8080/?$1$2;
return 200 foo;
}
Reported by Mufeed VH of Winfunc Research.
---
src/http/ngx_http_script.c | 20 +++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)
diff --git a/src/http/ngx_http_script.c b/src/http/ngx_http_script.c
index 2ea6113..8a28e23 100644
--- a/src/http/ngx_http_script.c
+++ b/src/http/ngx_http_script.c
@@ -1037,6 +1037,8 @@ ngx_http_script_start_args_code(ngx_http_script_engine_t *e)
void
ngx_http_script_regex_start_code(ngx_http_script_engine_t *e)
{
+ int *cap;
+ u_char *p;
size_t len;
ngx_int_t rc;
ngx_uint_t n;
@@ -1143,15 +1145,19 @@ ngx_http_script_regex_start_code(ngx_http_script_engine_t *e)
if (code->lengths == NULL) {
e->buf.len = code->size;
- if (code->uri) {
- if (r->ncaptures && (r->quoted_uri || r->plus_in_uri)) {
- e->buf.len += 2 * ngx_escape_uri(NULL, r->uri.data, r->uri.len,
- NGX_ESCAPE_ARGS);
- }
- }
+ cap = r->captures;
+ p = r->captures_data;
for (n = 2; n < r->ncaptures; n += 2) {
- e->buf.len += r->captures[n + 1] - r->captures[n];
+ e->buf.len += cap[n + 1] - cap[n];
+
+ if (code->uri) {
+ if (r->quoted_uri || r->plus_in_uri) {
+ e->buf.len += 2 * ngx_escape_uri(NULL, &p[cap[n]],
+ cap[n + 1] - cap[n],
+ NGX_ESCAPE_ARGS);
+ }
+ }
}
} else {
--
2.44.0

View File

@ -0,0 +1,319 @@
From 972780352ac6255796a877300e353484f04a148b Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Fri, 24 May 2024 00:20:01 +0300
Subject: [PATCH] Added max_headers directive.
The directive limits the number of request headers accepted from clients.
While the total amount of headers is believed to be sufficiently limited
by the existing buffer size limits (client_header_buffer_size and
large_client_header_buffers), the additional limit on the number of headers
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 <e.zupancic@f5.com>
Origin: <https://freenginx.org/hg/nginx/rev/199dc0d6b05be814b5c811876c20af58cd361fea>
---
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..c736d83 100644
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -8,6 +8,7 @@
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
+#include <ngx_http_header_count.hh>
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_header_count_conf_t, max_headers),
+ NULL },
+
+ 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);
+
+ return NGX_CONF_OK;
+}
+
+
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..88165b4 100644
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -8,6 +8,7 @@
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
+#include <ngx_http_header_count.hh>
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 (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");
+ ngx_http_finalize_request(r,
+ NGX_HTTP_REQUEST_HEADER_TOO_LARGE);
+ break;
+ }
+
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/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c
index 0f5bd3d..b340789 100644
--- a/src/http/v2/ngx_http_v2.c
+++ b/src/http/v2/ngx_http_v2.c
@@ -9,6 +9,7 @@
#include <ngx_core.h>
#include <ngx_http.h>
#include <ngx_http_v2_module.h>
+#include <ngx_http_header_count.hh>
/* 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 (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);
+ goto error;
+ }
+
h = ngx_list_push(&r->headers_in.headers);
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..a3e10ae 100644
--- a/src/http/v3/ngx_http_v3_request.c
+++ b/src/http/v3/ngx_http_v3_request.c
@@ -8,6 +8,7 @@
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
+#include <ngx_http_header_count.hh>
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 (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);
+ return NGX_ERROR;
+ }
+
h = ngx_list_push(&r->headers_in.headers);
if (h == NULL) {
ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
--
2.44.0

View File

@ -0,0 +1,99 @@
From 83cc0876c6d8f880b7c943e39d922e2286c94a41 Mon Sep 17 00:00:00 2001
From: Roman Arutyunyan <arut@nginx.com>
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

View File

@ -139,6 +139,18 @@ Patch10: 0011-Mp4-avoid-zero-size-buffers-in-output.patch
# upstream patch - https://github.com/nginx/nginx/commit/524977e7
Patch11: 0012-Rewrite-fixed-escaping-and-possible-buffer-overrun.patch
# https://redhat.atlassian.net/browse/RHEL-178669
# upstream patch - https://github.com/nginx/nginx/commit/ca4f92a27464ae6c2082245e4f67048c633aa032
Patch12: 0013-Rewrite-fix-buffer-overflow-with-overlapping-capture.patch
# https://redhat.atlassian.net/browse/RHEL-182544
# 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