[PATCH 2 of 5] Rewrite: removed optimized length calculations
Maxim Dounin
mdounin at mdounin.ru
Mon May 25 01:09:41 UTC 2026
# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1779656340 -10800
# Sun May 24 23:59:00 2026 +0300
# Node ID ebb65cf91321c0c58a8468e540dfebfdb8500322
# Parent c0b692d54bb5058b6886b53dbadb6b97aa988050
Rewrite: removed optimized length calculations.
Previously, ngx_http_script_regex_start_code() tried to use optimized
buffer length calculations when possible, without length codes
evaluation. Originally the code assumed that allocating space for all
the captures and escaping required for full URI is enough if variables
are not used. In 641:5e8fb59c18c1 (0.3.42) this was further refined to
require that duplicate captures are not used.
However, length calculations can be wrong when nested captures are used,
since the same URI character can appear in multiple captures and might
require escaping in all of them, leading to a buffer overflow, for
example (CVE-2026-9256):
rewrite ^/((.*)) /?c=$1&d=$2;
While it is possible to preserve and further refine optimized length
calculations, it is believed that a better solution would be to remove
them altogether, and this is what this change does.
See also:
https://github.com/nginx/nginx/commit/ca4f92a27464ae6c2082245e4f67048c633aa032
diff --git a/src/http/modules/ngx_http_rewrite_module.c b/src/http/modules/ngx_http_rewrite_module.c
--- a/src/http/modules/ngx_http_rewrite_module.c
+++ b/src/http/modules/ngx_http_rewrite_module.c
@@ -405,10 +405,6 @@ ngx_http_rewrite(ngx_conf_t *cf, ngx_com
regex->size = sc.size;
regex->args = sc.args;
- if (sc.variables == 0 && !sc.dup_capture) {
- regex->lengths = NULL;
- }
-
regex_end = ngx_http_script_add_code(lcf->codes,
sizeof(ngx_http_script_regex_end_code_t),
®ex);
diff --git a/src/http/ngx_http_script.c b/src/http/ngx_http_script.c
--- a/src/http/ngx_http_script.c
+++ b/src/http/ngx_http_script.c
@@ -483,12 +483,6 @@ ngx_http_script_compile(ngx_http_script_
n = sc->source->data[i] - '0';
- if (sc->captures_mask & ((ngx_uint_t) 1 << n)) {
- sc->dup_capture = 1;
- }
-
- sc->captures_mask |= (ngx_uint_t) 1 << n;
-
if (ngx_http_script_add_capture_code(sc, n) != NGX_OK) {
return NGX_ERROR;
}
@@ -1039,7 +1033,6 @@ ngx_http_script_regex_start_code(ngx_htt
{
size_t len;
ngx_int_t rc;
- ngx_uint_t n;
ngx_http_request_t *r;
ngx_http_script_engine_t le;
ngx_http_script_len_code_pt lcode;
@@ -1140,38 +1133,22 @@ ngx_http_script_regex_start_code(ngx_htt
}
}
- if (code->lengths == NULL) {
- e->buf.len = code->size;
+ ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
- 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);
- }
- }
-
- for (n = 2; n < r->ncaptures; n += 2) {
- e->buf.len += r->captures[n + 1] - r->captures[n];
- }
+ le.ip = code->lengths->elts;
+ le.line = e->line;
+ le.request = r;
+ le.quote = code->redirect;
+ le.is_args = e->is_args;
- } else {
- ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
-
- le.ip = code->lengths->elts;
- le.line = e->line;
- le.request = r;
- le.quote = code->redirect;
- le.is_args = e->is_args;
+ len = 0;
- len = 0;
+ while (*(uintptr_t *) le.ip) {
+ lcode = *(ngx_http_script_len_code_pt *) le.ip;
+ len += lcode(&le);
+ }
- while (*(uintptr_t *) le.ip) {
- lcode = *(ngx_http_script_len_code_pt *) le.ip;
- len += lcode(&le);
- }
-
- e->buf.len = len;
- }
+ e->buf.len = len;
if (code->add_args && r->args.len) {
e->buf.len += r->args.len + 1;
diff --git a/src/http/ngx_http_script.h b/src/http/ngx_http_script.h
--- a/src/http/ngx_http_script.h
+++ b/src/http/ngx_http_script.h
@@ -46,7 +46,6 @@ typedef struct {
ngx_uint_t variables;
ngx_uint_t ncaptures;
- ngx_uint_t captures_mask;
ngx_uint_t size;
void *main;
@@ -58,7 +57,6 @@ typedef struct {
unsigned conf_prefix:1;
unsigned root_prefix:1;
- unsigned dup_capture:1;
unsigned args:1;
} ngx_http_script_compile_t;
More information about the nginx-devel
mailing list