@@ -1200,6 +1200,40 @@ ngx_http_lua_ffi_pipe_proc_destroy(ngx_http_lua_ffi_pipe_proc_t *proc)
12001200 }
12011201
12021202 ngx_http_lua_pipe_proc_finalize (proc );
1203+
1204+ /*
1205+ * pipe_proc_finalize -> ngx_http_lua_pipe_close_helper may leave pipe
1206+ * connections open with active timers/posted events when there are
1207+ * pending I/O operations (handler != dummy_handler). Close them now
1208+ * before destroying the pool.
1209+ *
1210+ * Without this, when pool cleanup LIFO ordering causes pipe_proc_destroy
1211+ * to run before request_cleanup_handler (e.g. QUIC connection close path:
1212+ * ngx_quic_close_streams -> ngx_http_free_request -> ngx_destroy_pool),
1213+ * request_cleanup sees proc->pipe == NULL and returns early, leaving the
1214+ * timer live. The timer then fires after the request pool is freed,
1215+ * accessing a dangling wait_co_ctx pointer -> SIGSEGV.
1216+ *
1217+ * ngx_close_connection handles everything: timers (ngx_del_timer),
1218+ * posted events (ngx_delete_posted_event), epoll removal, fd close,
1219+ * and connection recycling.
1220+ */
1221+
1222+ if (pipe -> stdout_ctx && pipe -> stdout_ctx -> c ) {
1223+ ngx_close_connection (pipe -> stdout_ctx -> c );
1224+ pipe -> stdout_ctx -> c = NULL ;
1225+ }
1226+
1227+ if (pipe -> stderr_ctx && pipe -> stderr_ctx -> c ) {
1228+ ngx_close_connection (pipe -> stderr_ctx -> c );
1229+ pipe -> stderr_ctx -> c = NULL ;
1230+ }
1231+
1232+ if (pipe -> stdin_ctx && pipe -> stdin_ctx -> c ) {
1233+ ngx_close_connection (pipe -> stdin_ctx -> c );
1234+ pipe -> stdin_ctx -> c = NULL ;
1235+ }
1236+
12031237 ngx_destroy_pool (pipe -> pool );
12041238 proc -> pipe = NULL ;
12051239}
@@ -2495,13 +2529,20 @@ ngx_http_lua_pipe_proc_read_stdout_cleanup(void *data)
24952529 "lua pipe proc read stdout cleanup" );
24962530
24972531 proc = wait_co_ctx -> data ;
2532+
2533+ wait_co_ctx -> cleanup = NULL ;
2534+
2535+ if (proc -> pipe == NULL ) {
2536+ /* pipe_proc_destroy already ran (LIFO pool cleanup) and cancelled
2537+ * timers/connections; nothing left to clean up here. */
2538+ return ;
2539+ }
2540+
24982541 c = proc -> pipe -> stdout_ctx -> c ;
24992542 if (c ) {
25002543 rev = c -> read ;
25012544 ngx_http_lua_pipe_clear_event (rev );
25022545 }
2503-
2504- wait_co_ctx -> cleanup = NULL ;
25052546}
25062547
25072548
@@ -2517,13 +2558,18 @@ ngx_http_lua_pipe_proc_read_stderr_cleanup(void *data)
25172558 "lua pipe proc read stderr cleanup" );
25182559
25192560 proc = wait_co_ctx -> data ;
2561+
2562+ wait_co_ctx -> cleanup = NULL ;
2563+
2564+ if (proc -> pipe == NULL ) {
2565+ return ;
2566+ }
2567+
25202568 c = proc -> pipe -> stderr_ctx -> c ;
25212569 if (c ) {
25222570 rev = c -> read ;
25232571 ngx_http_lua_pipe_clear_event (rev );
25242572 }
2525-
2526- wait_co_ctx -> cleanup = NULL ;
25272573}
25282574
25292575
@@ -2539,13 +2585,18 @@ ngx_http_lua_pipe_proc_write_cleanup(void *data)
25392585 "lua pipe proc write cleanup" );
25402586
25412587 proc = wait_co_ctx -> data ;
2588+
2589+ wait_co_ctx -> cleanup = NULL ;
2590+
2591+ if (proc -> pipe == NULL ) {
2592+ return ;
2593+ }
2594+
25422595 c = proc -> pipe -> stdin_ctx -> c ;
25432596 if (c ) {
25442597 wev = c -> write ;
25452598 ngx_http_lua_pipe_clear_event (wev );
25462599 }
2547-
2548- wait_co_ctx -> cleanup = NULL ;
25492600}
25502601
25512602
@@ -2561,13 +2612,18 @@ ngx_http_lua_pipe_proc_wait_cleanup(void *data)
25612612 "lua pipe proc wait cleanup" );
25622613
25632614 proc = wait_co_ctx -> data ;
2615+
2616+ wait_co_ctx -> cleanup = NULL ;
2617+
2618+ if (proc -> pipe == NULL ) {
2619+ return ;
2620+ }
2621+
25642622 node = proc -> pipe -> node ;
25652623 pipe_node = (ngx_http_lua_pipe_node_t * ) & node -> color ;
25662624 pipe_node -> wait_co_ctx = NULL ;
25672625
25682626 ngx_http_lua_pipe_clear_event (& wait_co_ctx -> sleep );
2569-
2570- wait_co_ctx -> cleanup = NULL ;
25712627}
25722628
25732629
0 commit comments