Skip to content

Commit 0bc1765

Browse files
author
Erlend Egeberg Aasland
authored
[3.9] bpo-31746: Prevent segfaults when sqlite3.Connection is uninitialised (GH-27431) (GH-27465)
(cherry picked from commit 7e311e4) Co-authored-by: Erlend Egeberg Aasland <[email protected]>
1 parent c2c322b commit 0bc1765

File tree

2 files changed

+45
-8
lines changed

2 files changed

+45
-8
lines changed

Lib/sqlite3/test/dbapi.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,27 @@ def CheckSameThreadErrorOnOldVersion(self):
192192
sqlite.connect(':memory:', check_same_thread=False)
193193
self.assertEqual(str(cm.exception), 'shared connections not available')
194194

195+
196+
class UninitialisedConnectionTests(unittest.TestCase):
197+
def setUp(self):
198+
self.cx = sqlite.Connection.__new__(sqlite.Connection)
199+
200+
def test_uninit_operations(self):
201+
funcs = (
202+
lambda: self.cx.isolation_level,
203+
lambda: self.cx.total_changes,
204+
lambda: self.cx.in_transaction,
205+
lambda: self.cx.iterdump(),
206+
lambda: self.cx.cursor(),
207+
lambda: self.cx.close(),
208+
)
209+
for func in funcs:
210+
with self.subTest(func=func):
211+
self.assertRaisesRegex(sqlite.ProgrammingError,
212+
"Base Connection.__init__ not called",
213+
func)
214+
215+
195216
class CursorTests(unittest.TestCase):
196217
def setUp(self):
197218
self.cx = sqlite.connect(":memory:")
@@ -943,10 +964,11 @@ def suite():
943964
closed_con_suite = unittest.makeSuite(ClosedConTests, "Check")
944965
closed_cur_suite = unittest.makeSuite(ClosedCurTests, "Check")
945966
on_conflict_suite = unittest.makeSuite(SqliteOnConflictTests, "Check")
967+
uninit_con_suite = unittest.makeSuite(UninitialisedConnectionTests)
946968
return unittest.TestSuite((
947969
module_suite, connection_suite, cursor_suite, thread_suite,
948970
constructor_suite, ext_suite, closed_con_suite, closed_cur_suite,
949-
on_conflict_suite,
971+
on_conflict_suite, uninit_con_suite,
950972
))
951973

952974
def test():

Modules/_sqlite/connection.c

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,6 @@ int pysqlite_connection_init(pysqlite_Connection* self, PyObject* args, PyObject
102102

103103
database = PyBytes_AsString(database_obj);
104104

105-
self->initialized = 1;
106-
107105
self->begin_statement = NULL;
108106

109107
Py_CLEAR(self->statement_cache);
@@ -149,7 +147,7 @@ int pysqlite_connection_init(pysqlite_Connection* self, PyObject* args, PyObject
149147
Py_INCREF(isolation_level);
150148
}
151149
Py_CLEAR(self->isolation_level);
152-
if (pysqlite_connection_set_isolation_level(self, isolation_level, NULL) < 0) {
150+
if (pysqlite_connection_set_isolation_level(self, isolation_level, NULL) != 0) {
153151
Py_DECREF(isolation_level);
154152
return -1;
155153
}
@@ -208,6 +206,8 @@ int pysqlite_connection_init(pysqlite_Connection* self, PyObject* args, PyObject
208206
self->ProgrammingError = pysqlite_ProgrammingError;
209207
self->NotSupportedError = pysqlite_NotSupportedError;
210208

209+
self->initialized = 1;
210+
211211
return 0;
212212
}
213213

@@ -339,6 +339,12 @@ PyObject* pysqlite_connection_close(pysqlite_Connection* self, PyObject* args)
339339
return NULL;
340340
}
341341

342+
if (!self->initialized) {
343+
PyErr_SetString(pysqlite_ProgrammingError,
344+
"Base Connection.__init__ not called.");
345+
return NULL;
346+
}
347+
342348
pysqlite_do_all_statements(self, ACTION_FINALIZE, 1);
343349

344350
if (self->db) {
@@ -1143,6 +1149,9 @@ int pysqlite_check_thread(pysqlite_Connection* self)
11431149

11441150
static PyObject* pysqlite_connection_get_isolation_level(pysqlite_Connection* self, void* unused)
11451151
{
1152+
if (!pysqlite_check_connection(self)) {
1153+
return NULL;
1154+
}
11461155
Py_INCREF(self->isolation_level);
11471156
return self->isolation_level;
11481157
}
@@ -1175,11 +1184,17 @@ pysqlite_connection_set_isolation_level(pysqlite_Connection* self, PyObject* iso
11751184
return -1;
11761185
}
11771186
if (isolation_level == Py_None) {
1178-
PyObject *res = pysqlite_connection_commit(self, NULL);
1179-
if (!res) {
1180-
return -1;
1187+
/* We might get called during connection init, so we cannot use
1188+
* pysqlite_connection_commit() here. */
1189+
if (self->db && !sqlite3_get_autocommit(self->db)) {
1190+
int rc;
1191+
Py_BEGIN_ALLOW_THREADS
1192+
rc = sqlite3_exec(self->db, "COMMIT", NULL, NULL, NULL);
1193+
Py_END_ALLOW_THREADS
1194+
if (rc != SQLITE_OK) {
1195+
return _pysqlite_seterror(self->db, NULL);
1196+
}
11811197
}
1182-
Py_DECREF(res);
11831198

11841199
self->begin_statement = NULL;
11851200
} else {

0 commit comments

Comments
 (0)