From: Roman Arutyunyan Date: Mon, 29 Jun 2026 17:49:27 +0000 (+0400) Subject: Avoid duplicate subrequest finalization X-Git-Tag: release-1.31.3~1 X-Git-Url: http://git.kaiwu.me/http/$%7BGITURL%7D/doc/static/gitweb.js?a=commitdiff_plain;h=700dc9e0e750e3f63587f9d0f9f36bae5ec47202;p=nginx.git 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. --- diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 2859b5419..ed9a7e8ee 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -2655,6 +2655,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) { @@ -2665,8 +2673,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; @@ -2786,6 +2792,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; }