]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MEDIUM: stream: Fix a possible freeze during a forced shut on a stream
authorChristopher Faulet <cfaulet@haproxy.com>
Mon, 17 Mar 2025 13:49:45 +0000 (14:49 +0100)
committerChristopher Faulet <cfaulet@haproxy.com>
Thu, 3 Apr 2025 08:19:57 +0000 (10:19 +0200)
When a forced shutdown is performed on a stream, it is possible to freeze it
infinitly because it is performed in an unexpected way from process_stream()
point of view, especially when the stream is waiting for a server
connection. The events sequence is a bit complex but at the end the stream
remains blocked in turn-around state and no event are trriggered to unblock
it.

By trying to fix the issue, we considered it was safer to rethink the
feature. The idea is to quickly shutdown a stream to release resources. For
instance to be able to delete a server. So, instead of scheduling a
shutdown, it is more efficient to trigger an error and detach the stream
from the server, if neecessary. The same code than the one used to deal with
connection errors in back_handle_st_cer() is used.

This patch must be slowly backported as far as 2.6.

src/stream.c

index 78a5b7e638dfc05a91291d882d62b80a62d1cdae..d6ebe7cad1cc563f908e37d1ef5c2c7718980704 100644 (file)
@@ -2845,11 +2845,28 @@ void stream_shutdown_self(struct stream *stream, int why)
        if (stream->scb->flags & (SC_FL_SHUT_DONE|SC_FL_SHUT_WANTED))
                return;
 
-       sc_schedule_shutdown(stream->scb);
-       sc_schedule_abort(stream->scb);
        stream->task->nice = 1024;
        if (!(stream->flags & SF_ERR_MASK))
                stream->flags |= why;
+
+       if (objt_server(stream->target)) {
+               if (stream->flags & SF_CURR_SESS) {
+                       stream->flags &= ~SF_CURR_SESS;
+                       _HA_ATOMIC_DEC(&__objt_server(stream->target)->cur_sess);
+               }
+
+               sess_change_server(stream, NULL);
+               if (may_dequeue_tasks(objt_server(stream->target), stream->be))
+                       process_srv_queue(objt_server(stream->target));
+       }
+
+       /* shutw is enough to stop a connecting socket */
+       stream->scb->flags |= SC_FL_ERROR | SC_FL_NOLINGER;
+       sc_shutdown(stream->scb);
+
+       stream->scb->state = SC_ST_CLO;
+       if (stream->srv_error)
+               stream->srv_error(stream, stream->scb);
 }
 
 /* dumps an error message for type <type> at ptr <ptr> related to stream <s>,