From 263ffb9aacb52913c1b3dee455acdaeee1c1ff02 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Tue, 6 Oct 2026 22:42:00 +0200 Subject: [PATCH] BUG/MEDIUM: mux-fcgi: don't return a freed tasklet from fcgi_deferred_shut() Since 3.2 and commit 8f70f9c04 ("MEDIUM: mux_fcgi: Make sure we return the tasklet from fcgi_deferred_shut"), fcgi_deferred_shut() returns its tasklet to the scheduler. But when the deferred shutdown concerns an orphaned stream, fcgi_strm_destroy() is called, which frees the stream's shut_tl tasklet, which is precisely the one being run. The scheduler then clears TASK_RUNNING in the freed tasklet's state, which is a use-after-free write (and the connection may even have been released). This is the same issue as the one fixed in mux-h2 by commit "BUG/MEDIUM: mux-h2: don't return a freed tasklet from h2_deferred_shut()", where it was reproduced with -dMuaf. Here it's only based on code analysis. It requires that the FCGI_ABORT or the end of stream record could not be sent when the stream was shut because the mux buffer was full, and that the stream detaches before the deferred shutdown is performed, so it's rare and only the FCGI server side (trusted) is involved. The fix simply consists in returning NULL once the stream was destroyed. It must be backported to 3.2. --- src/mux_fcgi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mux_fcgi.c b/src/mux_fcgi.c index c01c5f057..cc92ac8ef 100644 --- a/src/mux_fcgi.c +++ b/src/mux_fcgi.c @@ -4005,7 +4005,9 @@ struct task *fcgi_deferred_shut(struct task *t, void *ctx, unsigned int state) LIST_DEL_INIT(&fstrm->send_list); if (!fcgi_strm_sc(fstrm)) { + /* note: this frees the tasklet as well */ fcgi_strm_destroy(fstrm); + t = NULL; if (fcgi_conn_is_dead(fconn)) fcgi_release(fconn); } -- 2.47.3