Skip to content

fix(gateway): serialize dashboard and cron SQLite access - #104

Draft
cursor[bot] wants to merge 1 commit into
mainfrom
cursor/high-severity-issues-124c
Draft

cursor[bot] wants to merge 1 commit into
mainfrom
cursor/high-severity-issues-124c

Conversation

@cursor

@cursor cursor Bot commented Sep 28, 2026

Copy link
Copy Markdown

Bug and impact

Vendored SQLite is compiled with SQLITE_THREADSAFE=0, so one sqlite3 * (g_db) must not be used from two threads. The gateway HTTP server runs on its own thread. These callers touched g_db without agent_lock():

  • GET /api/memory (memory_recall)
  • GET /api/sessions (session_list)
  • GET/POST /api/cron and cron delete/toggle
  • cron_poll on the main loop (cron_job_list_due / cron_job_get_by_id), which runs every poll even when no job is due

A concrete trigger: gateway enabled, dashboard open (it polls sessions, memory, and cron), and either an in-flight agent_run or the main-loop cron poll. Concurrent use of the connection is undefined. That can crash the process or corrupt memory.db (sessions, memories, and cron jobs).

DELETE /api/sessions/:id has the same missing lock. That path is already covered by #103 and is left unchanged here.

Root cause

#60 locked inbound ASAP state.query and mcp.tool_call, and handle_message locks agent_run / cron ack. The dashboard handlers and cron_poll were not updated to the same rule.

Fix

Hold agent_lock() around those SQL calls. cron_poll releases the lock before the re-offer sleep so a dashboard request is not blocked for the wait. The mutex is not recursive; tool execution inside agent_run still calls the SQL helpers directly and does not take the lock again.

Validation

  • tests/test_cron.c test_cron_poll_holds_agent_lock failed before the lock (SQLite entry while the mutex was free) and passes after.
  • tests/test_gateway_db_lock calls dispatch_route for memory, session list, and cron create/list/toggle/delete and requires agent_lock to be held on each SQL entry and released afterward.
  • ./build/test_cron, ./build/test_gateway_db_lock, ./build/test_dispatch, ./build/test_memory, ./build/test_agent, and ./build/test_asap_server passed.
Open in Web聽View Automation聽

The vendored SQLite build is SQLITE_THREADSAFE=0, and the gateway HTTP
thread shares that connection with the main loop. Dashboard memory,
session list, and cron routes, plus cron_poll's due-job query, touched
g_db without agent_lock. A dashboard refresh during a turn or a cron
poll is undefined and can crash the process or corrupt memory.db.

Co-authored-by: esadrianno <esadrianno@gmail.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant