Skip to content

Fix segfault and 404s with if blocks and inherited proxy_cache_purge - #69

Merged
denji merged 3 commits into
nginx-modules:masterfrom
macskas:fix-if-separate-purge-segfault
Sep 23, 2026
Merged

denji merged 3 commits into
nginx-modules:masterfrom
macskas:fix-if-separate-purge-segfault

Conversation

@macskas

@macskas macskas commented Sep 23, 2026

Copy link
Copy Markdown
  1. Segfault on PURGE in a location without proxy_cache (inline form,
    set directly or inherited from the server level).
    ngx_http_cache_purge_cache_get() passed a NULL cache_value to
    ngx_http_complex_value(). Seen in production on nginx 1.30.4:
    segfault at 18 ... in nginx (val->lengths). Now returns 404 and logs an error.

  2. The 3-arg form did not survive an if block. The anonymous if-child
    location did not inherit proxy_separate_zone/value/key, so e.g.
    if ($args != "") { set $params ?$args; } sent every request with a
    query string down the inline path: a segfault before fix 1, a 404 after it.

  3. Regression from 11b1c94: with proxy_cache_purge at the server level,
    every non-PURGE request to a proxy_pass location returned 404, because
    prev->original_handler (the server-level NULL) was inherited by named
    locations too. It is now inherited only by anonymous (if/limit_except)
    locations (clcf->noname). v3.0.2 is not affected.

Tested with t/Dockerfile: all five suites pass on nginx 1.28.2 and 1.30.4.
Without the patches, TEST 21-24 fail on current master.

The inline form (proxy_cache_purge PURGE from ...) is accepted in a
location that has no proxy_cache of its own, either written there
directly or inherited from the server level.  In such a location both
u->conf->cache_zone and u->conf->cache_value are NULL at request time,
and ngx_http_cache_purge_cache_get() passed the NULL complex value to
ngx_http_complex_value(), which dereferences it:

    nginx[1297]: segfault at 18 ip ... error 4 in nginx
    (ngx_http_complex_value+0x1b: mov 0x18(%rsi),%rdi -> val->lengths)

Return 404 and log an error instead.  All four callers (fastcgi, proxy,
scgi, uwsgi) pass the return code straight through as the response.
An "if" block inside a location creates an anonymous child location.
When the 3-argument form (proxy_cache_purge zone key) is used in the
parent, the child inherits proxy.enable == 0 but not the zone and key,
which live in proxy_separate_zone / proxy_separate_value /
proxy_separate_key.  r->content_handler is still the purge handler of
the parent, so a request that takes the if-branch runs the handler with
the child's config, fails the separate-syntax check and falls through
to the inline code path, where no cache is configured.

A common configuration triggers this for every request with a query
string:

    location ~ "/purge(?<path>/.*)$" {
        set $params "";
        if ($args != "") {
            set $params ?$args;
        }
        proxy_cache_purge zone GET$path$params;
    }

Without the previous commit this was a segfault; with it, a 404.
Copy the three fields from the parent in merge_loc_conf when the child
does not configure proxy_cache_purge itself.
…inherited

Commit 11b1c94 takes original_handler from the parent whenever the purge
directive was not set in the location itself, to handle the anonymous
child location that nginx creates for an "if" block.  The same condition
is true for every named location that inherits the directive from the
server level or from an enclosing location, for example:

    server {
        proxy_cache_purge PURGE from 127.0.0.1;

        location / {
            proxy_pass http://backend;
            proxy_cache zone;
        }
    }

There prev->original_handler is the server-level value, NULL, so every
non-PURGE request to "location /" returned 404 instead of being proxied.
v3.0.2 is not affected.

Take the parent's handler only for anonymous (if / limit_except)
locations, identified by clcf->noname.
@denji
denji merged commit c8d93de into nginx-modules:master Sep 23, 2026
11 checks passed
@denji

denji commented Sep 23, 2026

Copy link
Copy Markdown
Member

https://github.com/nginx-modules/ngx_cache_purge/releases/tag/3.0.3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants