gh-145040: Fix crash in sqlite3 when connection is closed from within a callback#145041
gh-145040: Fix crash in sqlite3 when connection is closed from within a callback#145041raminfp wants to merge 6 commits intopython:mainfrom
Conversation
…g aggregate callback Fix a segmentation fault in the _sqlite module that occurs when Connection.close() is called inside an aggregate step() callback. After stmt_step() returns, _pysqlite_query_execute() calls sqlite3_last_insert_rowid(self->connection->db) without checking if self->connection->db is still valid. If the connection was closed during the callback, self->connection->db is NULL, causing a NULL pointer dereference. The fix adds a NULL check for self->connection->db after stmt_step() returns, raising ProgrammingError instead of crashing.
Misc/NEWS.d/next/Library/2026-02-20-00-00-00.gh-issue-145040.sqlite3-close-crash.rst
Outdated
Show resolved
Hide resolved
…qlite3-close-crash.rst Co-authored-by: Benedikt Johannes <benedikt.johannes.hofer@gmail.com>
|
Please update the PR title to more accurately reflect the problem it addresses. Ditto for the NEWS item. |
…within a callback
4305a82 to
05086f0
Compare
|
Sorry for force push!!! |
|
This PR adds a sanity check after the damage has been done. The SQLite C API clearly tells us what's legal and illegal operations on the |
…lback Instead of detecting a closed connection after the damage has been done, prevent Connection.close() from succeeding while a SQLite callback is executing. This aligns with the SQLite C API docs, which state that applications must not close the database connection from within a callback. Add an in_callback counter to the connection object, incremented before stmt_step() and decremented after. If close() is called while the counter is positive, ProgrammingError is raised and the database connection remains open. A counter (rather than a boolean flag) is used to correctly handle nested callbacks. Also convert test docstrings to comments per reviewer feedback, and add a test for the nested callback scenario.
|
I agree, preventing the misuse upfront is the better approach. I've pushed a new commit that does exactly that:
The complexity turned out to be quite manageable, just a few lines in close(), and |
…ted callbacks Only check and consume the close_attempted_in_callback flag when in_callback reaches zero (the outermost level). Previously, a nested stmt_step() inside a callback could consume the flag, causing the outermost caller to miss the error.
Summary
Fix a segmentation fault (NULL pointer dereference) in the
_sqlitemodule that occurs whenConnection.close()is called inside an aggregatestep()callback.Fix
stmt_step(), check ifself->connection->db == NULL. If so, raiseProgrammingError("Cannot operate on a closed database.")and jump to the error path.sqlite3_last_insert_rowid()call withself->connection->db != NULLas an extra safety net.Test
Added
test_aggr_close_conn_in_stepinLib/test/test_sqlite3/test_userfunctions.pythat reproduces the exact crash scenario and verifies thatProgrammingErroris raised instead of a segfault.ASAN output (before fix)
After fix
_sqlite: NULL dereference when connection is closed from within a callback #145040