From 107f7778e217930769fe48b4990b86335787e616 Mon Sep 17 00:00:00 2001 From: Jabber Developer Date: Mon, 11 May 2026 20:54:42 +0000 Subject: [PATCH] fix(ai): collapse validate and dispatch into single critical section _aiwin_validate() no longer acquires the mutex. Callers now hold the lock across both the existence check and the display dispatch, eliminating the TOCTOU window where the window could be freed. --- src/ai/ai_client.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/ai/ai_client.c b/src/ai/ai_client.c index 30a02f9d..f49f1ef2 100644 --- a/src/ai/ai_client.c +++ b/src/ai/ai_client.c @@ -90,9 +90,7 @@ _aiwin_validate(gpointer user_data) return NULL; } - pthread_mutex_lock(&lock); gboolean win_exists = wins_ai_exists((ProfAiWin*)user_data); - pthread_mutex_unlock(&lock); if (!win_exists) { log_warning("[AI-THREAD] aiwin=%p no longer exists — window was closed", (void*)user_data); return NULL; @@ -110,12 +108,12 @@ _aiwin_validate(gpointer user_data) static void _aiwin_display_error(gpointer user_data, const gchar* error_msg) { + pthread_mutex_lock(&lock); ProfAiWin* aiwin = _aiwin_validate(user_data); if (!aiwin) { log_warning("[AI-THREAD] Cannot display error: aiwin is invalid or window was closed (msg: %s)", error_msg); return; } - pthread_mutex_lock(&lock); aiwin_display_error(aiwin, error_msg); pthread_mutex_unlock(&lock); } @@ -133,16 +131,14 @@ _aiwin_display_response(gpointer user_data, const gchar* response) return; } - /* Try to display in AI window if available */ + pthread_mutex_lock(&lock); ProfAiWin* aiwin = _aiwin_validate(user_data); if (!aiwin) { - pthread_mutex_lock(&lock); cons_show("%s", response); pthread_mutex_unlock(&lock); return; } - pthread_mutex_lock(&lock); aiwin_display_response(aiwin, response); pthread_mutex_unlock(&lock); }