Commit 14cbe0bf24 for strongswan.org

commit 14cbe0bf242319ec468b7b857030ef1095ad8af7
Author: seantywork <seantywork@gmail.com>
Date:   Sun Feb 15 09:40:49 2026 +0000

    whitelist: Fix deadlock when handling client disconnection

    Calling stream_t::destroy from the stream_t::on_read callback will
    block the thread in watcher_t::remove because the FD is currently "in
    callback".  A similar issue was fixed in the lookip plugin with
    961409b66858 ("lookip: Disconnect asynchronously to avoid dead-locking
    watcher unregistration").

    Fixes: 85ebf6abd441 ("whitelist: Add error handling to socket reads and fix a memory leak")

diff --git a/src/libcharon/plugins/whitelist/whitelist_control.c b/src/libcharon/plugins/whitelist/whitelist_control.c
index 4aec53aeef..37df7d9361 100644
--- a/src/libcharon/plugins/whitelist/whitelist_control.c
+++ b/src/libcharon/plugins/whitelist/whitelist_control.c
@@ -25,6 +25,7 @@

 #include <daemon.h>
 #include <collections/linked_list.h>
+#include <processing/jobs/callback_job.h>

 #include "whitelist_msg.h"

@@ -101,6 +102,40 @@ typedef struct {
 	size_t read;
 } whitelist_conn_t;

+/**
+ * Information needed for async disconnect job.
+ */
+typedef struct {
+	whitelist_conn_t *conn;
+	stream_t *stream;
+} disconnect_data_t;
+
+/**
+ * Asynchronous callback to disconnect client
+ */
+CALLBACK(disconnect_async, job_requeue_t,
+	disconnect_data_t *data)
+{
+	data->stream->destroy(data->stream);
+	free(data->conn);
+	return JOB_REQUEUE_NONE;
+}
+
+/**
+ * Disconnect a connected client
+ */
+static void disconnect(whitelist_conn_t *conn, stream_t *stream)
+{
+	disconnect_data_t *data;
+
+	INIT(data,
+		.conn = conn,
+		.stream = stream,
+	);
+	lib->processor->queue_job(lib->processor,
+			(job_t*)callback_job_create(disconnect_async, data, free, NULL));
+}
+
 /**
  * Dispatch a received message
  */
@@ -127,8 +162,7 @@ CALLBACK(on_read, bool,
 				{
 					DBG1(DBG_CFG, "whitelist socket error: %s", strerror(errno));
 				}
-				stream->destroy(stream);
-				free(conn);
+				disconnect(conn, stream);
 				return FALSE;
 			}
 			conn->read += len;