From ddde692db11ab8238e9ca661007f64c9f6d764d2 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 29 Jun 2026 21:49:27 +0400 Subject: [PATCH] Avoid duplicate subrequest finalization Previously, if a subrequest was posted twice, it could be finalized in both calls, excessively reducing r->main->count and potentially leading to a use-after-free. The fix is to avoid posting a request if it's already posted. Also, as a hardening measure, r->write_event_handler is now reset to a no-op handler during active subrequest finalization. The problem manifests itself in ngx_http_ssi_filter_module during unbuffered proxying. If a subrequest is created for an SSI include statement while the main request has some data postponed by another include, this subrequest becomes double-posted when the main request data is flushed. The first post comes from ngx_http_subrequest() and the second one comes from ngx_http_postpone_filter(). In case of a quick subrequest finalization, the above mentioned problem happens. Reported by P4P3R-HAK. --- src/http/ngx_http_request.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index a9573a620..c8dff236f 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -2628,6 +2628,14 @@ ngx_http_post_request(ngx_http_request_t *r, ngx_http_posted_request_t *pr) { ngx_http_posted_request_t **p; + for (p = &r->main->posted_requests; *p; p = &(*p)->next) { + if ((*p)->request == r) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http request already posted"); + return NGX_OK; + } + } + if (pr == NULL) { pr = ngx_palloc(r->pool, sizeof(ngx_http_posted_request_t)); if (pr == NULL) { @@ -2638,8 +2646,6 @@ ngx_http_post_request(ngx_http_request_t *r, ngx_http_posted_request_t *pr) pr->request = r; pr->next = NULL; - for (p = &r->main->posted_requests; *p; p = &(*p)->next) { /* void */ } - *p = pr; return NGX_OK; @@ -2759,6 +2765,8 @@ ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc) r->main->count--; + r->write_event_handler = ngx_http_request_empty_handler; + if (pr->postponed && pr->postponed->request == r) { pr->postponed = pr->postponed->next; } -- 2.47.3