Skip to content

Commit 86b5f1e

Browse files
authored
bugfix: prevent SIGSEGV in event timer rbtree during worker shutdown.
When a worker exits, ngx_worker_process_exit() calls ngx_destroy_pool(cycle->pool), which fires the cycle's cleanup handlers. One of these is ngx_http_lua_cleanup_vm, which calls lua_close() on the worker's Lua VM. lua_close() runs LuaJIT GC finalizers (__gc) on every live userdata, including TCP socket cosocket userdata still bound to in-flight requests. The __gc finalizers ngx_http_lua_socket_tcp_upstream_destroy and ngx_http_lua_socket_downstream_destroy reach ngx_http_lua_socket_tcp_finalize_{read,write}_part, which call ngx_del_timer() on connection events. By that point the event timer rbtree may already have been partially torn down by sibling finalizers, so ngx_del_timer() corrupts the tree and the next deletion segfaults inside ngx_rbtree_min / ngx_rbtree_delete. Skip the cleanup when ngx_terminate or ngx_exiting is set: the cycle pool is about to be destroyed and the kernel reclaims fds, so unwinding events and timers from the __gc path is unnecessary and unsafe. The same guard is already used in ngx_http_lua_socket_tcp_setkeepalive() for the analogous case. Reproduces under SIGTERM and SIGQUIT shutdown when there are in-flight ngx.socket.tcp() / ngx.req.socket() coroutines, e.g. long-lived ngx.sleep + tcpsock:receive() loops. Crash signature: ngx_rbtree_min src/core/ngx_rbtree.h ngx_rbtree_delete src/core/ngx_rbtree.c ngx_event_del_timer ngx_http_lua_socket_tcp_finalize_read_part ngx_http_lua_socket_tcp_finalize ngx_http_lua_socket_tcp_cleanup ngx_http_lua_socket_tcp_upstream_destroy (Lua __gc) ... LuaJIT GC sweep ... ngx_http_lua_cleanup_vm ngx_destroy_pool(cycle->pool) ngx_worker_process_exit
1 parent bbace59 commit 86b5f1e

1 file changed

Lines changed: 16 additions & 0 deletions

File tree

src/ngx_http_lua_socket_tcp.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6137,6 +6137,18 @@ ngx_http_lua_socket_tcp_upstream_destroy(lua_State *L)
61376137
return 0;
61386138
}
61396139

6140+
/*
6141+
* During worker shutdown the Lua VM is closed by the cycle pool
6142+
* cleanup; by then the event timer rbtree may already have been
6143+
* partially torn down by sibling finalizers. Calling ngx_del_timer
6144+
* from here corrupts the rbtree. The cycle pool is about to
6145+
* disappear and the kernel reclaims fds, so the cleanup is not
6146+
* needed at this point.
6147+
*/
6148+
if (ngx_terminate || ngx_exiting) {
6149+
return 0;
6150+
}
6151+
61406152
if (u->cleanup) {
61416153
ngx_http_lua_socket_tcp_cleanup(u); /* it will clear u->cleanup */
61426154
}
@@ -6158,6 +6170,10 @@ ngx_http_lua_socket_downstream_destroy(lua_State *L)
61586170
return 0;
61596171
}
61606172

6173+
if (ngx_terminate || ngx_exiting) {
6174+
return 0;
6175+
}
6176+
61616177
if (u->cleanup) {
61626178
ngx_http_lua_socket_tcp_cleanup(u); /* it will clear u->cleanup */
61636179
}

0 commit comments

Comments
 (0)