Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Lib/test/test_sqlite3/test_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
# 3. This notice may not be removed or altered from any source distribution.

import unittest
import weakref
import sqlite3 as sqlite
from collections.abc import Sequence

Expand Down Expand Up @@ -156,6 +157,17 @@ def test_delete_connection_text_factory(self):
with self.assertRaises(AttributeError):
del self.con.text_factory

def test_replace_active_row_factory(self):
class Factory:
def row(self, cursor, values):
cursor.row_factory = None
return factory_ref() is not None

cursor = self.con.cursor()
cursor.row_factory = Factory().row
factory_ref = weakref.ref(cursor.row_factory)
self.assertIs(cursor.execute("select 1").fetchone(), True)

def test_delete_cursor_row_factory(self):
# gh-149738: deleting row_factory should raise an exception
cur = self.con.cursor()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix crashes in :mod:`sqlite3` when a row or text factory is replaced during a
query, when cursor metadata is read during execution in a free-threaded build,
or when allocating cursor metadata fails.
29 changes: 18 additions & 11 deletions Modules/_sqlite/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,9 @@ pysqlite_connection_init_impl(pysqlite_Connection *self, PyObject *database,
self->thread_ident = PyThread_get_thread_ident();
self->statement_cache = statement_cache;
self->blobs = blobs;
self->row_factory = Py_NewRef(Py_None);
self->text_factory = Py_NewRef(&PyUnicode_Type);
pysqlite_set_ref((PyObject *)self, &self->row_factory, Py_NewRef(Py_None));
pysqlite_set_ref((PyObject *)self, &self->text_factory,
Py_NewRef(&PyUnicode_Type));
self->trace_ctx = NULL;
self->progress_ctx = NULL;
self->authorizer_ctx = NULL;
Expand Down Expand Up @@ -411,8 +412,8 @@ connection_clear(PyObject *op)
pysqlite_Connection *self = _pysqlite_Connection_CAST(op);
Py_CLEAR(self->statement_cache);
Py_CLEAR(self->blobs);
Py_CLEAR(self->row_factory);
Py_CLEAR(self->text_factory);
pysqlite_set_ref(op, &self->row_factory, NULL);
pysqlite_set_ref(op, &self->text_factory, NULL);
clear_callback_context(self->trace_ctx);
clear_callback_context(self->progress_ctx);
clear_callback_context(self->authorizer_ctx);
Expand Down Expand Up @@ -554,9 +555,14 @@ pysqlite_connection_cursor_impl(pysqlite_Connection *self, PyObject *factory)
return NULL;
}

if (cursor && self->row_factory != Py_None) {
Py_INCREF(self->row_factory);
Py_XSETREF(((pysqlite_Cursor *)cursor)->row_factory, self->row_factory);
PyObject *row_factory = pysqlite_get_ref((PyObject *)self,
&self->row_factory, Py_None);
if (row_factory != Py_None) {
pysqlite_Cursor *cur = (pysqlite_Cursor *)cursor;
pysqlite_set_ref(cursor, &cur->row_factory, row_factory);
}
else {
Py_DECREF(row_factory);
}

return cursor;
Expand All @@ -566,7 +572,7 @@ static PyObject *
connection_get_row_factory(PyObject *op, void *closure)
{
pysqlite_Connection *self = (pysqlite_Connection *)op;
return Py_NewRef(self->row_factory);
return pysqlite_get_ref(op, &self->row_factory, Py_None);
}

static int
Expand All @@ -578,15 +584,16 @@ connection_set_row_factory(PyObject *op, PyObject *value, void *closure)
"cannot delete row_factory attribute");
return -1;
}
Py_XSETREF(self->row_factory, Py_NewRef(value));
pysqlite_set_ref(op, &self->row_factory, Py_NewRef(value));
return 0;
}

static PyObject *
connection_get_text_factory(PyObject *op, void *closure)
{
pysqlite_Connection *self = (pysqlite_Connection *)op;
return Py_NewRef(self->text_factory);
return pysqlite_get_ref(op, &self->text_factory,
(PyObject *)&PyUnicode_Type);
}

static int
Expand All @@ -598,7 +605,7 @@ connection_set_text_factory(PyObject *op, PyObject *value, void *closure)
"cannot delete text_factory attribute");
return -1;
}
Py_XSETREF(self->text_factory, Py_NewRef(value));
pysqlite_set_ref(op, &self->text_factory, Py_NewRef(value));
return 0;
}

Expand Down
58 changes: 34 additions & 24 deletions Modules/_sqlite/cursor.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,18 +121,15 @@ pysqlite_cursor_init_impl(pysqlite_Cursor *self,
Py_CLEAR(self->statement);
Py_CLEAR(self->row_cast_map);

Py_INCREF(Py_None);
Py_XSETREF(self->description, Py_None);
pysqlite_set_ref((PyObject *)self, &self->description, Py_NewRef(Py_None));

Py_INCREF(Py_None);
Py_XSETREF(self->lastrowid, Py_None);
pysqlite_set_ref((PyObject *)self, &self->lastrowid, Py_NewRef(Py_None));

self->arraysize = 1;
self->closed = 0;
self->rowcount = -1L;

Py_INCREF(Py_None);
Py_XSETREF(self->row_factory, Py_None);
pysqlite_set_ref((PyObject *)self, &self->row_factory, Py_NewRef(Py_None));

if (!pysqlite_check_thread(self->connection)) {
return -1;
Expand Down Expand Up @@ -176,10 +173,10 @@ cursor_clear(PyObject *op)
{
pysqlite_Cursor *self = _pysqlite_Cursor_CAST(op);
Py_CLEAR(self->connection);
Py_CLEAR(self->description);
pysqlite_set_ref(op, &self->description, NULL);
Py_CLEAR(self->row_cast_map);
Py_CLEAR(self->lastrowid);
Py_CLEAR(self->row_factory);
pysqlite_set_ref(op, &self->lastrowid, NULL);
pysqlite_set_ref(op, &self->row_factory, NULL);
if (self->statement) {
/* Reset the statement if the user has not closed the cursor */
int rc = stmt_reset(self->statement);
Expand Down Expand Up @@ -415,12 +412,17 @@ _pysqlite_fetch_one_row(pysqlite_Cursor* self)
}

nbytes = sqlite3_column_bytes(self->statement->st, i);
if (self->connection->text_factory == (PyObject*)&PyUnicode_Type) {
PyObject *factory = pysqlite_get_ref(
(PyObject *)self->connection,
&self->connection->text_factory,
(PyObject *)&PyUnicode_Type);
if (factory == (PyObject*)&PyUnicode_Type) {
converted = PyUnicode_FromStringAndSize(text, nbytes);
if (!converted && PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)) {
PyErr_Clear();
colname = sqlite3_column_name(self->statement->st, i);
if (colname == NULL) {
Py_DECREF(factory);
PyErr_NoMemory();
goto error;
}
Expand All @@ -436,13 +438,14 @@ _pysqlite_fetch_one_row(pysqlite_Cursor* self)
Py_DECREF(error_msg);
}
}
} else if (self->connection->text_factory == (PyObject*)&PyBytes_Type) {
} else if (factory == (PyObject*)&PyBytes_Type) {
converted = PyBytes_FromStringAndSize(text, nbytes);
} else if (self->connection->text_factory == (PyObject*)&PyByteArray_Type) {
} else if (factory == (PyObject*)&PyByteArray_Type) {
converted = PyByteArray_FromStringAndSize(text, nbytes);
} else {
converted = PyObject_CallFunction(self->connection->text_factory, "y#", text, nbytes);
converted = PyObject_CallFunction(factory, "y#", text, nbytes);
}
Py_DECREF(factory);
} else {
/* coltype == SQLITE_BLOB */
const void *blob = sqlite3_column_blob(self->statement->st, i);
Expand Down Expand Up @@ -797,6 +800,7 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation
PyObject* parameters_list = NULL;
PyObject* parameters_iter = NULL;
PyObject* parameters = NULL;
PyObject* description = NULL;
int i;
int rc;
int numcols;
Expand Down Expand Up @@ -846,8 +850,7 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation
}

/* reset description */
Py_INCREF(Py_None);
Py_SETREF(self->description, Py_None);
pysqlite_set_ref((PyObject *)self, &self->description, Py_NewRef(Py_None));

if (self->statement) {
// Reset pending statements on this cursor.
Expand Down Expand Up @@ -931,8 +934,8 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation
numcols = sqlite3_column_count(self->statement->st);
Py_END_ALLOW_THREADS
if (self->description == Py_None && numcols > 0) {
Py_SETREF(self->description, PyTuple_New(numcols));
if (!self->description) {
description = PyTuple_New(numcols);
if (description == NULL) {
goto error;
}
for (i = 0; i < numcols; i++) {
Expand All @@ -953,8 +956,10 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation
if (descriptor == NULL) {
goto error;
}
PyTuple_SET_ITEM(self->description, i, descriptor);
PyTuple_SET_ITEM(description, i, descriptor);
}
pysqlite_set_ref((PyObject *)self, &self->description, description);
description = NULL;
}

if (rc == SQLITE_DONE) {
Expand All @@ -975,11 +980,15 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation
lastrowid = sqlite3_last_insert_rowid(self->connection->db);
Py_END_ALLOW_THREADS

Py_SETREF(self->lastrowid, PyLong_FromLongLong(lastrowid));
// Fall through on error.
PyObject *lastrowid_obj = PyLong_FromLongLong(lastrowid);
if (lastrowid_obj == NULL) {
goto error;
}
pysqlite_set_ref((PyObject *)self, &self->lastrowid, lastrowid_obj);
}

error:
Py_XDECREF(description);
Py_XDECREF(parameters);
Py_XDECREF(parameters_iter);
Py_XDECREF(parameters_list);
Expand Down Expand Up @@ -1177,12 +1186,13 @@ pysqlite_cursor_iternext(PyObject *op)
}
return NULL;
}
if (!Py_IsNone(self->row_factory)) {
PyObject *factory = self->row_factory;
PyObject *factory = pysqlite_get_ref(op, &self->row_factory, Py_None);
if (!Py_IsNone(factory)) {
PyObject *args[] = { op, row, };
PyObject *new_row = PyObject_Vectorcall(factory, args, 2, NULL);
Py_SETREF(row, new_row);
}
Py_DECREF(factory);
return row;

reset_failure:
Expand Down Expand Up @@ -1411,7 +1421,7 @@ static PyObject *
cursor_get_row_factory(PyObject *op, void *Py_UNUSED(closure))
{
pysqlite_Cursor *self = _pysqlite_Cursor_CAST(op);
return Py_NewRef(self->row_factory);
return pysqlite_get_ref(op, &self->row_factory, Py_None);
}

static int
Expand All @@ -1423,7 +1433,7 @@ cursor_set_row_factory(PyObject *op, PyObject *value, void *Py_UNUSED(closure))
"cannot delete row_factory attribute");
return -1;
}
Py_XSETREF(self->row_factory, Py_NewRef(value));
pysqlite_set_ref(op, &self->row_factory, Py_NewRef(value));
return 0;
}

Expand Down
4 changes: 3 additions & 1 deletion Modules/_sqlite/row.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

#include "row.h"
#include "cursor.h"
#include "util.h"

#define clinic_state() (pysqlite_get_state_by_type(type))
#include "clinic/row.c.h"
Expand Down Expand Up @@ -93,7 +94,8 @@ pysqlite_row_new_impl(PyTypeObject *type, pysqlite_Cursor *cursor,
return NULL;

self->data = Py_NewRef(data);
self->description = Py_NewRef(cursor->description);
self->description = pysqlite_get_ref((PyObject *)cursor,
&cursor->description, Py_None);

return (PyObject *) self;
}
Expand Down
25 changes: 25 additions & 0 deletions Modules/_sqlite/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#ifndef PYSQLITE_UTIL_H
#define PYSQLITE_UTIL_H
#include "Python.h"
#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_STORE_PTR_RELEASE()
#include "pythread.h"
#include "sqlite3.h"
#include "connection.h"
Expand All @@ -36,4 +37,28 @@ void set_error_from_code(pysqlite_state *state, int code);

sqlite_int64 _pysqlite_long_as_int64(PyObject * value);

/* Take a strong reference while the owning object's lock is held. */
static inline PyObject *
pysqlite_get_ref(PyObject *owner, PyObject *const *field, PyObject *fallback)
{
PyObject *value;
Py_BEGIN_CRITICAL_SECTION(owner);
value = Py_NewRef(*field != NULL ? *field : fallback);
Py_END_CRITICAL_SECTION();
return value;
}

/* Steal value. Use the same store as PyMember_SetOne() since a readonly
* member descriptor may load the field without taking the object's lock. */
static inline void
pysqlite_set_ref(PyObject *owner, PyObject **field, PyObject *value)
{
PyObject *old;
Py_BEGIN_CRITICAL_SECTION(owner);
old = *field;
FT_ATOMIC_STORE_PTR_RELEASE(*field, value);
Py_END_CRITICAL_SECTION();
Py_XDECREF(old);
}

#endif
Loading