[3.10] bpo-31746: Prevent segfaults when sqlite3.Connection is uninitialised (GH-27431). (GH-27472)
https://github.com/python/cpython/commit/0cb470e622ba37920c72e4d8f284741b9fb... commit: 0cb470e622ba37920c72e4d8f284741b9fbaea8b branch: 3.10 author: Erlend Egeberg Aasland <erlend.aasland@innova.no> committer: ambv <lukasz@langa.pl> date: 2021-07-30T14:01:22+02:00 summary: [3.10] bpo-31746: Prevent segfaults when sqlite3.Connection is uninitialised (GH-27431). (GH-27472) (cherry picked from commit 7e311e496b0e26b3d3c62fe9b0ed2a4677c37ee9) Co-authored-by: Erlend Egeberg Aasland <erlend.aasland@innova.no> files: M Lib/sqlite3/test/dbapi.py M Modules/_sqlite/connection.c diff --git a/Lib/sqlite3/test/dbapi.py b/Lib/sqlite3/test/dbapi.py index a958d4df8bb0c8..2d02045b72e8d6 100644 --- a/Lib/sqlite3/test/dbapi.py +++ b/Lib/sqlite3/test/dbapi.py @@ -197,6 +197,26 @@ def test_open_uri(self): cx.execute('insert into test(id) values(1)') +class UninitialisedConnectionTests(unittest.TestCase): + def setUp(self): + self.cx = sqlite.Connection.__new__(sqlite.Connection) + + def test_uninit_operations(self): + funcs = ( + lambda: self.cx.isolation_level, + lambda: self.cx.total_changes, + lambda: self.cx.in_transaction, + lambda: self.cx.iterdump(), + lambda: self.cx.cursor(), + lambda: self.cx.close(), + ) + for func in funcs: + with self.subTest(func=func): + self.assertRaisesRegex(sqlite.ProgrammingError, + "Base Connection.__init__ not called", + func) + + class CursorTests(unittest.TestCase): def setUp(self): self.cx = sqlite.connect(":memory:") @@ -949,6 +969,7 @@ def suite(): ModuleTests, SqliteOnConflictTests, ThreadTests, + UninitialisedConnectionTests, ] return unittest.TestSuite( [unittest.TestLoader().loadTestsFromTestCase(t) for t in tests] diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 95f6029d126756..9c05a154559d92 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -92,8 +92,6 @@ pysqlite_connection_init(pysqlite_Connection *self, PyObject *args, database = PyBytes_AsString(database_obj); - self->initialized = 1; - self->begin_statement = NULL; Py_CLEAR(self->statement_cache); @@ -128,7 +126,7 @@ pysqlite_connection_init(pysqlite_Connection *self, PyObject *args, Py_INCREF(isolation_level); } Py_CLEAR(self->isolation_level); - if (pysqlite_connection_set_isolation_level(self, isolation_level, NULL) < 0) { + if (pysqlite_connection_set_isolation_level(self, isolation_level, NULL) != 0) { Py_DECREF(isolation_level); return -1; } @@ -187,6 +185,8 @@ pysqlite_connection_init(pysqlite_Connection *self, PyObject *args, return -1; } + self->initialized = 1; + return 0; } @@ -359,6 +359,12 @@ pysqlite_connection_close_impl(pysqlite_Connection *self) return NULL; } + if (!self->initialized) { + PyErr_SetString(pysqlite_ProgrammingError, + "Base Connection.__init__ not called."); + return NULL; + } + pysqlite_do_all_statements(self, ACTION_FINALIZE, 1); if (self->db) { @@ -1264,6 +1270,9 @@ int pysqlite_check_thread(pysqlite_Connection* self) static PyObject* pysqlite_connection_get_isolation_level(pysqlite_Connection* self, void* unused) { + if (!pysqlite_check_connection(self)) { + return NULL; + } return Py_NewRef(self->isolation_level); } @@ -1295,11 +1304,17 @@ pysqlite_connection_set_isolation_level(pysqlite_Connection* self, PyObject* iso return -1; } if (isolation_level == Py_None) { - PyObject *res = pysqlite_connection_commit(self, NULL); - if (!res) { - return -1; + /* We might get called during connection init, so we cannot use + * pysqlite_connection_commit() here. */ + if (self->db && !sqlite3_get_autocommit(self->db)) { + int rc; + Py_BEGIN_ALLOW_THREADS + rc = sqlite3_exec(self->db, "COMMIT", NULL, NULL, NULL); + Py_END_ALLOW_THREADS + if (rc != SQLITE_OK) { + return _pysqlite_seterror(self->db, NULL); + } } - Py_DECREF(res); self->begin_statement = NULL; } else {
participants (1)
-
ambv