Skip to content

Commit 60fdb31

Browse files
authored
gh-149738: Fix segmentation fault bug in sqllite3 (#149754)
Deleting the `row_factory` or `text_factory` attribute is no longer allowed.
1 parent 5a2d273 commit 60fdb31

4 files changed

Lines changed: 66 additions & 2 deletions

File tree

Doc/library/sqlite3.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,6 +1417,9 @@ Connection objects
14171417

14181418
See :ref:`sqlite3-howto-row-factory` for more details.
14191419

1420+
.. versionchanged:: next
1421+
Deleting the ``row_factory`` attribute is no longer allowed.
1422+
14201423
.. attribute:: text_factory
14211424

14221425
A :term:`callable` that accepts a :class:`bytes` parameter
@@ -1426,6 +1429,9 @@ Connection objects
14261429

14271430
See :ref:`sqlite3-howto-encoding` for more details.
14281431

1432+
.. versionchanged:: next
1433+
Deleting the ``text_factory`` attribute is no longer allowed.
1434+
14291435
.. attribute:: total_changes
14301436

14311437
Return the total number of database rows that have been modified, inserted, or
@@ -1709,6 +1715,9 @@ Cursor objects
17091715

17101716
See :ref:`sqlite3-howto-row-factory` for more details.
17111717

1718+
.. versionchanged:: next
1719+
Deleting the ``row_factory`` attribute is no longer allowed.
1720+
17121721

17131722
.. The sqlite3.Row example used to be a how-to. It has now been incorporated
17141723
into the Row reference. We keep the anchor here in order not to break

Lib/test/test_sqlite3/test_factory.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,16 @@ def test_sqlite_row_index(self):
146146
with self.assertRaises(IndexError):
147147
row[complex()] # index must be int or string
148148

149+
def test_delete_connection_row_factory(self):
150+
# gh-149738: deleting row_factory should raise an exception
151+
with self.assertRaises(AttributeError):
152+
del self.con.row_factory
153+
154+
def test_delete_connection_text_factory(self):
155+
# gh-149738: deleting text_factory should raise an exception
156+
with self.assertRaises(AttributeError):
157+
del self.con.text_factory
158+
149159
def test_sqlite_row_index_unicode(self):
150160
row = self.con.execute("select 1 as \xff").fetchone()
151161
self.assertEqual(row["\xff"], 1)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:mod:`sqlite3`: Disallow removing ``row_factory`` and ``text_factory`` attributes
2+
of a connection to prevent a crash on a query.

Modules/_sqlite/connection.c

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,47 @@ pysqlite_connection_cursor_impl(pysqlite_Connection *self, PyObject *factory)
557557
return cursor;
558558
}
559559

560+
static PyObject *
561+
connection_get_row_factory(PyObject *op, void *closure)
562+
{
563+
pysqlite_Connection *self = (pysqlite_Connection *)op;
564+
return Py_NewRef(self->row_factory);
565+
}
566+
567+
static int
568+
connection_set_row_factory(PyObject *op, PyObject *value, void *closure)
569+
{
570+
pysqlite_Connection *self = (pysqlite_Connection *)op;
571+
if (value == NULL) {
572+
PyErr_SetString(PyExc_AttributeError,
573+
"cannot delete row_factory attribute");
574+
return -1;
575+
}
576+
Py_XSETREF(self->row_factory, Py_NewRef(value));
577+
return 0;
578+
}
579+
580+
static PyObject *
581+
connection_get_text_factory(PyObject *op, void *closure)
582+
{
583+
pysqlite_Connection *self = (pysqlite_Connection *)op;
584+
return Py_NewRef(self->text_factory);
585+
}
586+
587+
static int
588+
connection_set_text_factory(PyObject *op, PyObject *value, void *closure)
589+
{
590+
pysqlite_Connection *self = (pysqlite_Connection *)op;
591+
if (value == NULL) {
592+
PyErr_SetString(PyExc_AttributeError,
593+
"cannot delete text_factory attribute");
594+
return -1;
595+
}
596+
Py_XSETREF(self->text_factory, Py_NewRef(value));
597+
return 0;
598+
}
599+
600+
560601
/*[clinic input]
561602
_sqlite3.Connection.blobopen as blobopen
562603
@@ -2620,6 +2661,10 @@ static PyGetSetDef connection_getset[] = {
26202661
{"in_transaction", pysqlite_connection_get_in_transaction, NULL},
26212662
{"autocommit", get_autocommit, set_autocommit},
26222663
{"__text_signature__", get_sig, NULL},
2664+
{"row_factory", connection_get_row_factory,
2665+
connection_set_row_factory},
2666+
{"text_factory", connection_get_text_factory,
2667+
connection_set_text_factory},
26232668
{NULL}
26242669
};
26252670

@@ -2667,8 +2712,6 @@ static struct PyMemberDef connection_members[] =
26672712
{"InternalError", _Py_T_OBJECT, offsetof(pysqlite_Connection, InternalError), Py_READONLY},
26682713
{"ProgrammingError", _Py_T_OBJECT, offsetof(pysqlite_Connection, ProgrammingError), Py_READONLY},
26692714
{"NotSupportedError", _Py_T_OBJECT, offsetof(pysqlite_Connection, NotSupportedError), Py_READONLY},
2670-
{"row_factory", _Py_T_OBJECT, offsetof(pysqlite_Connection, row_factory)},
2671-
{"text_factory", _Py_T_OBJECT, offsetof(pysqlite_Connection, text_factory)},
26722715
{NULL}
26732716
};
26742717

0 commit comments

Comments
 (0)