From 14e864b71c4ff6e7d93794a4a02837e27fb7311e Mon Sep 17 00:00:00 2001 From: Jarvis Date: Fri, 21 Aug 2026 09:58:18 +0000 Subject: [PATCH] fix: close the connection before destroying its pool ngx_http_multi_upstream_connection_close() destroyed c->pool and then called ngx_close_connection(c). ngx_close_connection() still logs through c->log, and for these upstream connections c->log lives in the pool that was just released, so the debug-level "reusable connection: %ui" from ngx_reusable_connection() reads a freed ngx_log_t and dereferences a garbage file descriptor. Reorder to match ngx_http_close_connection(): mark the connection destroyed, take the pool aside, close, then destroy the pool. Both callers in ngx_http_multi_upstream.c treat the close as the final statement, so nothing observes the connection afterwards. Reproduces as a worker SIGSEGV on APISIX's t/plugin/dubbo-proxy/upstream.t TEST 1 with a debug-enabled build: #0 ngx_write_fd (fd=) #1 ngx_log_error_core (fmt="reusable connection: %ui") #2 ngx_reusable_connection (c=..., reusable=0) #3 ngx_close_connection (c=...) #4 ngx_http_multi_upstream_connection_close (c=...) at ngx_http_multi_upstream_module.c:755 --- ngx_http_multi_upstream_module.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/ngx_http_multi_upstream_module.c b/ngx_http_multi_upstream_module.c index fd336d1..597965c 100644 --- a/ngx_http_multi_upstream_module.c +++ b/ngx_http_multi_upstream_module.c @@ -727,6 +727,8 @@ ngx_http_multi_upstream_connection_detach(ngx_connection_t *c) ngx_int_t ngx_http_multi_upstream_connection_close(ngx_connection_t *c) { + ngx_pool_t *pool; + #if (NGX_HTTP_SSL) /* TODO: do not shutdown persistent connection */ if (c->ssl) { @@ -746,14 +748,20 @@ ngx_http_multi_upstream_connection_close(ngx_connection_t *c) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "multi: close http upstream connection: %d", c->fd); - if (c->pool) { - ngx_destroy_pool(c->pool); - } - c->destroyed = 1; + /* ngx_close_connection() still logs through c->log, which lives in c->pool, + * so the pool has to outlive it - the same order ngx_http_close_connection() + * uses */ + pool = c->pool; + c->pool = NULL; + ngx_close_connection(c); + if (pool) { + ngx_destroy_pool(pool); + } + return NGX_OK; }