From 2cf1c76a7076f909f463ffc5cc5b6c59ba6e3be6 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 20 Aug 2026 14:27:46 +0300 Subject: [PATCH 1/3] gh-156100: Fix a crash when deleting sqlite3 Connection.autocommit The setter passed the deleted value to autocommit_converter(), which dereferences it. --- Lib/test/test_sqlite3/test_transactions.py | 7 +++++++ .../Library/2026-08-20-12-05-00.gh-issue-156100.Lm7qWz.rst | 3 +++ Modules/_sqlite/connection.c | 5 +++++ 3 files changed, 15 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-08-20-12-05-00.gh-issue-156100.Lm7qWz.rst diff --git a/Lib/test/test_sqlite3/test_transactions.py b/Lib/test/test_sqlite3/test_transactions.py index a3de7a7a82ec1c..59ddfc5e1dfea3 100644 --- a/Lib/test/test_sqlite3/test_transactions.py +++ b/Lib/test/test_sqlite3/test_transactions.py @@ -394,6 +394,13 @@ def test_autocommit_setget_invalid(self): with self.assertRaisesRegex(ValueError, msg): sqlite.connect(":memory:", autocommit=mode) + def test_autocommit_delete(self): + with memory_database() as cx: + with self.assertRaisesRegex(AttributeError, + "cannot delete autocommit attribute"): + del cx.autocommit + self.assertEqual(cx.autocommit, sqlite.LEGACY_TRANSACTION_CONTROL) + def test_autocommit_disabled(self): expected = [ "SELECT 1", diff --git a/Misc/NEWS.d/next/Library/2026-08-20-12-05-00.gh-issue-156100.Lm7qWz.rst b/Misc/NEWS.d/next/Library/2026-08-20-12-05-00.gh-issue-156100.Lm7qWz.rst new file mode 100644 index 00000000000000..48465d3fcc2fe9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-08-20-12-05-00.gh-issue-156100.Lm7qWz.rst @@ -0,0 +1,3 @@ +Fix a crash when deleting the :attr:`~sqlite3.Connection.autocommit` +attribute of :class:`sqlite3.Connection`. +It now raises :exc:`AttributeError`. diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 892740b05e55c9..c453c6c9d15ee9 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -2621,6 +2621,11 @@ static int set_autocommit(PyObject *op, PyObject *val, void *Py_UNUSED(closure)) { pysqlite_Connection *self = _pysqlite_Connection_CAST(op); + if (val == NULL) { + PyErr_SetString(PyExc_AttributeError, + "cannot delete autocommit attribute"); + return -1; + } if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { return -1; } From 5af0c34e8a9e02877da55788ac6c4deb5d6586e3 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 20 Aug 2026 14:50:36 +0300 Subject: [PATCH 2/3] Check that a failed deletion does not change the value --- Lib/test/test_sqlite3/test_transactions.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_sqlite3/test_transactions.py b/Lib/test/test_sqlite3/test_transactions.py index 59ddfc5e1dfea3..c8e294e2e5be0c 100644 --- a/Lib/test/test_sqlite3/test_transactions.py +++ b/Lib/test/test_sqlite3/test_transactions.py @@ -396,10 +396,12 @@ def test_autocommit_setget_invalid(self): def test_autocommit_delete(self): with memory_database() as cx: + cx.autocommit = False with self.assertRaisesRegex(AttributeError, "cannot delete autocommit attribute"): del cx.autocommit - self.assertEqual(cx.autocommit, sqlite.LEGACY_TRANSACTION_CONTROL) + # a failed deletion does not change the value + self.assertIs(cx.autocommit, False) def test_autocommit_disabled(self): expected = [ From 3b4b844a1a29ef8cd8686e61f40809c64e4a1ad4 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 20 Aug 2026 15:06:26 +0300 Subject: [PATCH 3/3] Fix setting autocommit to an out of range integer LEGACY_TRANSACTION_CONTROL is -1, the same value which PyLong_AsLong() returns on error, so the converter reported success with OverflowError set. --- Lib/test/test_sqlite3/test_transactions.py | 8 +++++++- ...2026-08-20-12-05-00.gh-issue-156100.Lm7qWz.rst | 7 ++++--- Modules/_sqlite/connection.c | 15 ++++++++++----- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/Lib/test/test_sqlite3/test_transactions.py b/Lib/test/test_sqlite3/test_transactions.py index c8e294e2e5be0c..2e5d60fe9ba5fc 100644 --- a/Lib/test/test_sqlite3/test_transactions.py +++ b/Lib/test/test_sqlite3/test_transactions.py @@ -389,10 +389,16 @@ def test_autocommit_setget(self): def test_autocommit_setget_invalid(self): msg = "autocommit must be True, False, or.*LEGACY" - for mode in "a", 12, (), None: + for mode in "a", 12, (), None, 2**1000, -2**1000: with self.subTest(mode=mode): with self.assertRaisesRegex(ValueError, msg): sqlite.connect(":memory:", autocommit=mode) + with memory_database() as cx: + with self.assertRaisesRegex(ValueError, msg): + cx.autocommit = mode + # a failed assignment does not change the value + self.assertEqual(cx.autocommit, + sqlite.LEGACY_TRANSACTION_CONTROL) def test_autocommit_delete(self): with memory_database() as cx: diff --git a/Misc/NEWS.d/next/Library/2026-08-20-12-05-00.gh-issue-156100.Lm7qWz.rst b/Misc/NEWS.d/next/Library/2026-08-20-12-05-00.gh-issue-156100.Lm7qWz.rst index 48465d3fcc2fe9..8c296a9a8919fc 100644 --- a/Misc/NEWS.d/next/Library/2026-08-20-12-05-00.gh-issue-156100.Lm7qWz.rst +++ b/Misc/NEWS.d/next/Library/2026-08-20-12-05-00.gh-issue-156100.Lm7qWz.rst @@ -1,3 +1,4 @@ -Fix a crash when deleting the :attr:`~sqlite3.Connection.autocommit` -attribute of :class:`sqlite3.Connection`. -It now raises :exc:`AttributeError`. +Fix crashes in :class:`sqlite3.Connection` when deleting the +:attr:`~sqlite3.Connection.autocommit` attribute or setting it to an integer +which does not fit in C :c:expr:`long`. +Both now raise an exception. diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index c453c6c9d15ee9..ec47471873f782 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -104,11 +104,16 @@ autocommit_converter(PyObject *val, enum autocommit_mode *result) *result = AUTOCOMMIT_DISABLED; return 1; } - if (PyLong_Check(val) && - PyLong_AsLong(val) == LEGACY_TRANSACTION_CONTROL) - { - *result = AUTOCOMMIT_LEGACY; - return 1; + if (PyLong_Check(val)) { + int overflow; + long value = PyLong_AsLongAndOverflow(val, &overflow); + if (value == -1 && PyErr_Occurred()) { + return 0; + } + if (!overflow && value == LEGACY_TRANSACTION_CONTROL) { + *result = AUTOCOMMIT_LEGACY; + return 1; + } } PyErr_SetString(PyExc_ValueError,