From: Vsevolod Stakhov Date: Thu, 22 Jan 2026 13:40:20 +0000 (+0000) Subject: [Fix] Stop HTTP watchers before error handlers X-Git-Tag: 4.0.0~181 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c2a2479d25353cb46acb288fa8b0d6bb7920db2a;p=thirdparty%2Frspamd.git [Fix] Stop HTTP watchers before error handlers --- diff --git a/src/libserver/http/http_connection.c b/src/libserver/http/http_connection.c index e9afe47167..d998d8ae35 100644 --- a/src/libserver/http/http_connection.c +++ b/src/libserver/http/http_connection.c @@ -770,6 +770,9 @@ rspamd_http_simple_client_helper(struct rspamd_http_connection *conn) } } +static inline void +rspamd_http_connection_stop_watcher(struct rspamd_http_connection_private *priv); + static void rspamd_http_write_helper(struct rspamd_http_connection *conn) { @@ -856,6 +859,7 @@ rspamd_http_write_helper(struct rspamd_http_connection *conn) err = g_error_new(HTTP_ERROR, 400, "HTTP parser error on early response: %s", http_errno_description(priv->parser.http_errno)); + rspamd_http_connection_stop_watcher(priv); rspamd_http_connection_ref(conn); conn->error_handler(conn, err); rspamd_http_connection_unref(conn); @@ -870,6 +874,7 @@ rspamd_http_write_helper(struct rspamd_http_connection *conn) } err = g_error_new(HTTP_ERROR, 500, "IO write error: %s", strerror(saved_errno)); + rspamd_http_connection_stop_watcher(priv); rspamd_http_connection_ref(conn); conn->error_handler(conn, err); rspamd_http_connection_unref(conn); @@ -981,12 +986,22 @@ static void rspamd_http_ssl_err_handler(gpointer ud, GError *err) { struct rspamd_http_connection *conn = (struct rspamd_http_connection *) ud; + struct rspamd_http_connection_private *priv = conn->priv; + + /* Error is terminal, stop watcher before delegating to user handler */ + rspamd_http_connection_stop_watcher(priv); rspamd_http_connection_ref(conn); conn->error_handler(conn, err); rspamd_http_connection_unref(conn); } +static inline void +rspamd_http_connection_stop_watcher(struct rspamd_http_connection_private *priv) +{ + rspamd_ev_watcher_stop(priv->ctx->event_loop, &priv->ev); +} + static void rspamd_http_event_handler(int fd, short what, gpointer ud) { @@ -1015,6 +1030,7 @@ rspamd_http_event_handler(int fd, short what, gpointer ud) sock_err != 0) { err = g_error_new(HTTP_ERROR, 500, "Connection failed: %s", strerror(sock_err)); + rspamd_http_connection_stop_watcher(priv); conn->error_handler(conn, err); g_error_free(err); @@ -1069,6 +1085,7 @@ rspamd_http_event_handler(int fd, short what, gpointer ud) } if (!conn->finished) { + rspamd_http_connection_stop_watcher(priv); conn->error_handler(conn, err); } else { @@ -1091,6 +1108,7 @@ rspamd_http_event_handler(int fd, short what, gpointer ud) err = g_error_new(HTTP_ERROR, 400, "IO read error: unexpected EOF"); + rspamd_http_connection_stop_watcher(priv); conn->error_handler(conn, err); g_error_free(err); } @@ -1105,6 +1123,7 @@ rspamd_http_event_handler(int fd, short what, gpointer ud) 500, "HTTP IO read error: %s", strerror(errno)); + rspamd_http_connection_stop_watcher(priv); conn->error_handler(conn, err); g_error_free(err); } @@ -1129,6 +1148,7 @@ rspamd_http_event_handler(int fd, short what, gpointer ud) http_errno_description(priv->parser.http_errno)); if (!conn->finished) { + rspamd_http_connection_stop_watcher(priv); conn->error_handler(conn, err); } else { @@ -1146,6 +1166,7 @@ rspamd_http_event_handler(int fd, short what, gpointer ud) else { err = g_error_new(HTTP_ERROR, 408, "IO timeout"); + rspamd_http_connection_stop_watcher(priv); conn->error_handler(conn, err); g_error_free(err); diff --git a/src/libserver/rspamd_control.c b/src/libserver/rspamd_control.c index 17e53abc34..16993bb49b 100644 --- a/src/libserver/rspamd_control.c +++ b/src/libserver/rspamd_control.c @@ -164,6 +164,11 @@ rspamd_control_connection_close(struct rspamd_control_session *session) } rspamd_inet_address_free(session->addr); + /* + * Stop any pending IO watcher before closing the socket; otherwise libev can + * abort on ev_io_stop() during final unref in some reload/startup races. + */ + rspamd_http_connection_reset(session->conn); rspamd_http_connection_unref(session->conn); close(session->fd); g_free(session);