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
961409b668 ("lookip: Disconnect asynchronously to avoid dead-locking
watcher unregistration").

Fixes: 85ebf6abd4 ("whitelist: Add error handling to socket reads and fix a memory leak")
This commit is contained in:
seantywork
2026-02-20 12:44:55 +01:00
committed by Tobias Brunner
parent 8a6f9ba70e
commit 14cbe0bf24
@@ -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;