From: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> Date: Sun, 29 Mar 2026 12:21:37 +0000 (+0200) Subject: gh-146090: fix memory management of internal `sqlite3` callback contexts (#146569) X-Git-Tag: v3.15.0a8~124 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=aa6680775d6d9ca571a675c3b2d655f4ade78c0c;p=thirdparty%2FPython%2Fcpython.git gh-146090: fix memory management of internal `sqlite3` callback contexts (#146569) --- diff --git a/Lib/test/test_sqlite3/test_hooks.py b/Lib/test/test_sqlite3/test_hooks.py index 495ef97fa3c6..7e5117771b37 100644 --- a/Lib/test/test_sqlite3/test_hooks.py +++ b/Lib/test/test_sqlite3/test_hooks.py @@ -120,6 +120,21 @@ class CollationTests(MemoryDatabaseMixin, unittest.TestCase): self.assertEqual(result[0][0], 'b') self.assertEqual(result[1][0], 'a') + def test_collation_register_when_busy(self): + # See https://github.com/python/cpython/issues/146090. + con = self.con + con.create_collation("mycoll", lambda x, y: (x > y) - (x < y)) + con.execute("CREATE TABLE t(x TEXT)") + con.execute("INSERT INTO t VALUES (?)", ("a",)) + con.execute("INSERT INTO t VALUES (?)", ("b",)) + con.commit() + + cursor = self.con.execute("SELECT x FROM t ORDER BY x COLLATE mycoll") + next(cursor) + # Replace the collation while the statement is active -> SQLITE_BUSY. + with self.assertRaises(sqlite.OperationalError) as cm: + self.con.create_collation("mycoll", lambda a, b: 0) + def test_deregister_collation(self): """ Register a collation, then deregister it. Make sure an error is raised if we try diff --git a/Misc/NEWS.d/next/Library/2026-03-28-12-01-48.gh-issue-146090.wh1qJR.rst b/Misc/NEWS.d/next/Library/2026-03-28-12-01-48.gh-issue-146090.wh1qJR.rst new file mode 100644 index 000000000000..a6d60d2c9293 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-03-28-12-01-48.gh-issue-146090.wh1qJR.rst @@ -0,0 +1,2 @@ +:mod:`sqlite3`: properly raise :exc:`MemoryError` instead of :exc:`SystemError` +when a context callback fails to be allocated. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2026-03-28-12-05-34.gh-issue-146090.wf9_ef.rst b/Misc/NEWS.d/next/Library/2026-03-28-12-05-34.gh-issue-146090.wf9_ef.rst new file mode 100644 index 000000000000..5b835b0271a6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-03-28-12-05-34.gh-issue-146090.wf9_ef.rst @@ -0,0 +1,3 @@ +:mod:`sqlite3`: fix a crash when :meth:`sqlite3.Connection.create_collation` +fails with `SQLITE_BUSY `__. Patch by +Bénédikt Tran. diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index af63271b9fd9..bd44ff31b87c 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -1059,13 +1059,16 @@ static callback_context * create_callback_context(PyTypeObject *cls, PyObject *callable) { callback_context *ctx = PyMem_Malloc(sizeof(callback_context)); - if (ctx != NULL) { - PyObject *module = PyType_GetModule(cls); - ctx->refcount = 1; - ctx->callable = Py_NewRef(callable); - ctx->module = Py_NewRef(module); - ctx->state = pysqlite_get_state(module); + if (ctx == NULL) { + PyErr_NoMemory(); + return NULL; } + + PyObject *module = PyType_GetModule(cls); + ctx->refcount = 1; + ctx->callable = Py_NewRef(callable); + ctx->module = Py_NewRef(module); + ctx->state = pysqlite_get_state(module); return ctx; } @@ -2198,7 +2201,7 @@ pysqlite_connection_create_collation_impl(pysqlite_Connection *self, * the context before returning. */ if (callable != Py_None) { - free_callback_context(ctx); + decref_callback_context(ctx); } set_error_from_db(self->state, self->db); return NULL;