Fix(ai): protect AI session state from concurrent access races
Some checks failed
CI Code / Check spelling (pull_request) Successful in 20s
CI Code / Check coding style (pull_request) Successful in 35s
CI Code / Linux (debian) (pull_request) Failing after 3m47s
CI Code / Linux (ubuntu) (pull_request) Failing after 3m54s
CI Code / Code Coverage (pull_request) Successful in 6m41s
CI Code / Linux (arch) (pull_request) Failing after 9m31s

cmd_ai_switch() and cmd_ai_clear() mutated session fields (provider_name,
provider, model, api_key, history) on the main thread without coordination,
while _ai_request_thread() read the same fields concurrently in the worker
thread. This caused use-after-free when:

- g_free(session->model) was called between worker's read and g_strdup()
- ai_provider_unref(session->provider) freed the provider struct while
  worker accessed session->provider->api_url
- ai_session_clear_history() freed the history list while worker walked it

Fix:
1. Add pthread_mutex_t lock to AISession struct to protect all session
   fields from concurrent access.

2. Introduce ai_session_switch() public API that atomically switches
   provider, model, and API key under the session lock. This encapsulates
   all session mutations within ai_client.c so cmd_funcs.c never touches
   session fields directly.

3. Have _ai_request_thread() snapshot all session state under the session
   lock before making requests, using local copies for the duration of
   the curl request.

4. Add ai_provider_ref()/ai_provider_unref() around _ai_generic_request_thread()
   to prevent provider UAF during model-fetch requests.

5. Protect ai_session_add_message(), ai_session_clear_history(), and
   ai_session_set_model() with the session lock.

No pthread.h needed in cmd_funcs.c — all locking is encapsulated within
ai_client.c via the public API. No deadlock risk from nested locks.

Files changed:
- src/ai/ai_client.h: Add lock field, ai_session_switch() declaration
- src/ai/ai_client.c: Mutex init/destroy, session mutation protection,
  worker thread snapshot, provider ref management
- src/command/cmd_funcs.c: Use ai_session_switch() API, remove pthread.h
This commit is contained in:
2026-05-11 23:42:24 +00:00
parent 9f4621883a
commit a75a06b4a2
3 changed files with 42 additions and 47 deletions

View File

@@ -239,32 +239,6 @@ const gchar* ai_session_get_model(AISession* session);
*/
void ai_session_set_model(AISession* session, const gchar* model);
/**
* Atomically switch session provider, model, and API key.
* All mutations happen under the session lock to prevent races with
* _ai_request_thread() which snapshots session state before making requests.
*
* @param session The session
* @param provider_name New provider name
* @param model New model identifier
* @param api_key New API key (caller must free after calling this)
*/
void ai_session_switch(AISession* session, const gchar* provider_name,
const gchar* model, gchar* api_key);
/**
* Atomically switch session provider, model, and API key.
* All mutations happen under the session lock to prevent races with
* _ai_request_thread() which snapshots session state before making requests.
*
* @param session The session
* @param provider_name New provider name
* @param model New model identifier
* @param api_key New API key (caller must free after calling this)
*/
void ai_session_switch(AISession* session, const gchar* provider_name,
const gchar* model, gchar* api_key);
/**
* Atomically switch session provider, model, and API key.
* All mutations happen under the session lock to prevent races with