[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),
                                       &regex);
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