[PATCH] Fixed positional captures with cloned subrequests after non-match

Maxim Dounin mdounin at mdounin.ru
Sun Aug 2 20:10:49 UTC 2026


# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1785701313 -10800
#      Sun Aug 02 23:08:33 2026 +0300
# Node ID da23d6e6186c5a11b082d894d0107d90761d8079
# Parent  42952827f5a9cc7cf589844bcb6afd1eedd13d50
Fixed positional captures with cloned subrequests after non-match.

In 7427:81d49f85afed, the r->realloc_captures flag was introduced, to
facilitate use of positional captures with cloned subrequests, as used
by proxy_cache_background_update and the slice module.  The flag,
however, was only used to trigger a new allocation of r->captures, and
it contained uninitialized if a regular expression was not matched.  As
a result, if a regular expression was executed in a cloned subrequest
and not matched, and then a positional capture was used, it might refer
to arbitrary memory, such as in the following configuration (known as
CVE-2026-60005, though security impact is questionable):

    map $uri $map {
        ~(not-matched) 1;
    }

    location ~ /regex/(foo) {
        proxy_pass ...
        proxy_cache ...
        proxy_cache_background_update on;
        proxy_set_header Foo $map:$1;
    }

The fix is to actually copy r->captures contents to the new allocation,
so positional captures properly refer to the last matched regular
expression.

See also:
https://github.com/nginx/nginx/commit/0cca8e055a2d909f1a00c2071665b502ec2fe94c

diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -2657,6 +2657,7 @@ ngx_http_regex_compile(ngx_conf_t *cf, n
 ngx_int_t
 ngx_http_regex_exec(ngx_http_request_t *r, ngx_http_regex_t *re, ngx_str_t *s)
 {
+    int                        *captures;
     ngx_int_t                   rc, index;
     ngx_uint_t                  i, n, len;
     ngx_http_variable_value_t   vv;
@@ -2670,10 +2671,16 @@ ngx_http_regex_exec(ngx_http_request_t *
         if (r->captures == NULL || r->realloc_captures) {
             r->realloc_captures = 0;
 
-            r->captures = ngx_palloc(r->pool, len * sizeof(int));
-            if (r->captures == NULL) {
+            captures = ngx_pcalloc(r->pool, len * sizeof(int));
+            if (captures == NULL) {
                 return NGX_ERROR;
             }
+
+            if (r->captures) {
+                ngx_memcpy(captures, r->captures, len * sizeof(int));
+            }
+
+            r->captures = captures;
         }
 
     } else {



More information about the nginx-devel mailing list