[nginx] Rewrite: removed optimized length calculations.

Maxim Dounin mdounin at mdounin.ru
Tue Jun 2 16:11:50 UTC 2026


details:   http://freenginx.org/hg/nginx/rev/dbf061c1f4f2
branches:  stable-1.30
changeset: 9547:dbf061c1f4f2
user:      Maxim Dounin <mdounin at mdounin.ru>
date:      Tue May 26 03:18:34 2026 +0300
description:
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

diffstat:

 src/http/modules/ngx_http_rewrite_module.c |   4 --
 src/http/ngx_http_script.c                 |  47 +++++++----------------------
 src/http/ngx_http_script.h                 |   2 -
 3 files changed, 12 insertions(+), 41 deletions(-)

diffs (108 lines):

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