]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MAJOR: applet: Don't call I/O handler if the applet was shut
authorChristopher Faulet <cfaulet@haproxy.com>
Thu, 22 Jan 2026 13:40:45 +0000 (14:40 +0100)
committerChristopher Faulet <cfaulet@haproxy.com>
Tue, 27 Jan 2026 15:00:23 +0000 (16:00 +0100)
In 3.0, it was stated an applet could not be woken up after it was shutdown.
So the corresponding test in the applets I/O handler was removed. However,
it seems it may happen, especially when outgoing data are blocked on the
opposite side. But it is really unexpected because the "release" callback
function was already called and the appctx context was most probably
released.

Strangely, it was never detected by any applet till now. But the Prometheus
exporter was never updated and was still testing the shutdown. But when it
was refactored to use the new applet API in 3.3, the test was removed. And
this introduced a regression leading a crash because a server object could
be corrupted. Conditions to hit the bug are not really clear however.

So, now, to avoid any issue with all other applets, the test is performed in
task_process_applet(). The I/O handler is no longer called if the applet is
already shut.

The same is performed for applets still relying on the old API.

An amazing thanks to @idl0r for his invaluable help on this issue !

This patch should fix the issue #3244. It should first be backported to 3.3
and then slowly as far as 3.0.

src/applet.c

index 75d714c9bcc52addad2dbc1fa07b4cbd46956d54..de555c3250b7bce34d0501d7a1179f9347e50588 100644 (file)
@@ -848,7 +848,12 @@ struct task *task_run_applet(struct task *t, void *context, unsigned int state)
 
        input  = applet_output_data(app);
        output = co_data(oc);
-       app->applet->fct(app);
+
+       /* Don't call I/O handler if the applet was shut (release callback was
+        * already called)
+        */
+       if (se_fl_test(app->sedesc, SE_FL_SHR | SE_FL_SHW))
+               app->applet->fct(app);
 
        TRACE_POINT(APPLET_EV_PROCESS, app);
 
@@ -945,7 +950,11 @@ struct task *task_process_applet(struct task *t, void *context, unsigned int sta
        applet_need_more_data(app);
        applet_have_no_more_data(app);
 
-       app->applet->fct(app);
+       /* Don't call I/O handler if the applet was shut (release callback was
+        * already called)
+        */
+       if (!applet_fl_test(app, APPCTX_FL_SHUTDOWN))
+               app->applet->fct(app);
 
        TRACE_POINT(APPLET_EV_PROCESS, app);