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.
This commit is contained in:
2026-05-11 20:54:42 +00:00
parent b634eed5af
commit 107f7778e2

View File

@@ -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);
}