From: Willy Tarreau Date: Tue, 6 Oct 2026 20:42:00 +0000 (+0200) Subject: BUG/MEDIUM: mux-fcgi: don't return a freed tasklet from fcgi_deferred_shut() X-Git-Tag: v3.5-dev9~36 X-Git-Url: https://git.kaiwu.me/$%7BISSUES%7D/static/gitweb.js?a=commitdiff_plain;h=263ffb9aacb52913c1b3dee455acdaeee1c1ff02;p=haproxy.git 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. --- 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); }