Skip to content

Commit a2fb885

Browse files
fix: add null checks in sqlite
1 parent b643826 commit a2fb885

3 files changed

Lines changed: 27 additions & 6 deletions

File tree

Modules/_sqlite/connection.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,10 @@ connection_close(pysqlite_Connection *self)
456456
sqlite3 *db = self->db;
457457
self->db = NULL;
458458

459+
/* Unregister callbacks before closing so that SQLite cannot invoke them
460+
* again after free_callback_contexts releases their contexts. */
461+
remove_callbacks(db);
462+
459463
Py_BEGIN_ALLOW_THREADS
460464
/* The v2 close call always returns SQLITE_OK if given a valid database
461465
* pointer (which we do), so we can safely ignore the return value */

Modules/_sqlite/cursor.c

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,12 @@ _pysqlite_fetch_one_row(pysqlite_Cursor* self)
360360
return NULL;
361361

362362
sqlite3 *db = self->connection->db;
363+
if (db == NULL) {
364+
pysqlite_state *state = self->connection->state;
365+
PyErr_SetString(state->ProgrammingError,
366+
"Cannot operate on a closed database.");
367+
goto error;
368+
}
363369
for (i = 0; i < numcols; i++) {
364370
if (self->connection->detect_types
365371
&& self->row_cast_map != NULL
@@ -530,10 +536,16 @@ begin_transaction(pysqlite_Connection *self)
530536
static PyObject *
531537
get_statement_from_cache(pysqlite_Cursor *self, PyObject *operation)
532538
{
533-
PyObject *args[] = { NULL, operation, }; // Borrowed ref.
534-
PyObject *cache = self->connection->statement_cache;
539+
PyObject *args[] = { NULL, operation, };
540+
541+
/* Hold a strong reference: a Python callback invoked during statement
542+
* preparation (e.g. an authorizer) may close the connection, which calls
543+
* Py_CLEAR(connection->statement_cache). */
544+
PyObject *cache = Py_NewRef(self->connection->statement_cache);
535545
size_t nargsf = 1 | PY_VECTORCALL_ARGUMENTS_OFFSET;
536-
return PyObject_Vectorcall(cache, args + 1, nargsf, NULL);
546+
PyObject *result = PyObject_Vectorcall(cache, args + 1, nargsf, NULL);
547+
Py_DECREF(cache);
548+
return result;
537549
}
538550

539551
static inline int
@@ -957,7 +969,7 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation
957969
}
958970

959971
if (rc == SQLITE_DONE) {
960-
if (self->statement->is_dml) {
972+
if (self->statement->is_dml && self->connection->db) {
961973
self->rowcount += (long)sqlite3_changes(self->connection->db);
962974
}
963975
if (stmt_reset(self->statement) != SQLITE_OK) {
@@ -967,7 +979,7 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation
967979
Py_XDECREF(parameters);
968980
}
969981

970-
if (!multiple) {
982+
if (!multiple && self->connection->db) {
971983
sqlite_int64 lastrowid;
972984

973985
Py_BEGIN_ALLOW_THREADS
@@ -1157,7 +1169,7 @@ pysqlite_cursor_iternext(PyObject *op)
11571169
}
11581170
int rc = stmt_step(stmt);
11591171
if (rc == SQLITE_DONE) {
1160-
if (self->statement->is_dml) {
1172+
if (self->statement->is_dml && self->connection->db) {
11611173
self->rowcount = (long)sqlite3_changes(self->connection->db);
11621174
}
11631175
rc = stmt_reset(self->statement);

Modules/_sqlite/util.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@ set_error_from_code(pysqlite_state *state, int code)
138138
int
139139
set_error_from_db(pysqlite_state *state, sqlite3 *db)
140140
{
141+
if (db == NULL) {
142+
PyErr_SetString(state->ProgrammingError,
143+
"Cannot operate on a closed database.");
144+
return SQLITE_MISUSE;
145+
}
141146
int errorcode = sqlite3_errcode(db);
142147
PyObject *exc_class = get_exception_class(state, errorcode);
143148
if (exc_class == NULL) {

0 commit comments

Comments
 (0)