Skip to content

Commit 7e311e4

Browse files
author
Erlend Egeberg Aasland
authored
bpo-31746: Prevent segfaults when sqlite3.Connection is uninitialised (GH-27431)
1 parent 8182c83 commit 7e311e4

File tree

2 files changed

+44
-7
lines changed

2 files changed

+44
-7
lines changed

Lib/sqlite3/test/dbapi.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,26 @@ def test_drop_unused_refs(self):
243243
self.assertEqual(cu.fetchone()[0], n)
244244

245245

246+
class UninitialisedConnectionTests(unittest.TestCase):
247+
def setUp(self):
248+
self.cx = sqlite.Connection.__new__(sqlite.Connection)
249+
250+
def test_uninit_operations(self):
251+
funcs = (
252+
lambda: self.cx.isolation_level,
253+
lambda: self.cx.total_changes,
254+
lambda: self.cx.in_transaction,
255+
lambda: self.cx.iterdump(),
256+
lambda: self.cx.cursor(),
257+
lambda: self.cx.close(),
258+
)
259+
for func in funcs:
260+
with self.subTest(func=func):
261+
self.assertRaisesRegex(sqlite.ProgrammingError,
262+
"Base Connection.__init__ not called",
263+
func)
264+
265+
246266
class OpenTests(unittest.TestCase):
247267
_sql = "create table test(id integer)"
248268

@@ -951,6 +971,7 @@ def suite():
951971
ModuleTests,
952972
SqliteOnConflictTests,
953973
ThreadTests,
974+
UninitialisedConnectionTests,
954975
]
955976
return unittest.TestSuite(
956977
[unittest.TestLoader().loadTestsFromTestCase(t) for t in tests]

Modules/_sqlite/connection.c

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,6 @@ pysqlite_connection_init_impl(pysqlite_Connection *self,
111111

112112
const char *database = PyBytes_AsString(database_obj);
113113

114-
self->initialized = 1;
115-
116114
self->begin_statement = NULL;
117115

118116
Py_CLEAR(self->statement_cache);
@@ -147,7 +145,7 @@ pysqlite_connection_init_impl(pysqlite_Connection *self,
147145
Py_INCREF(isolation_level);
148146
}
149147
Py_CLEAR(self->isolation_level);
150-
if (pysqlite_connection_set_isolation_level(self, isolation_level, NULL) < 0) {
148+
if (pysqlite_connection_set_isolation_level(self, isolation_level, NULL) != 0) {
151149
Py_DECREF(isolation_level);
152150
return -1;
153151
}
@@ -195,6 +193,8 @@ pysqlite_connection_init_impl(pysqlite_Connection *self,
195193
return -1;
196194
}
197195

196+
self->initialized = 1;
197+
198198
return 0;
199199
}
200200

@@ -371,6 +371,13 @@ pysqlite_connection_close_impl(pysqlite_Connection *self)
371371
return NULL;
372372
}
373373

374+
if (!self->initialized) {
375+
pysqlite_state *state = pysqlite_get_state(NULL);
376+
PyErr_SetString(state->ProgrammingError,
377+
"Base Connection.__init__ not called.");
378+
return NULL;
379+
}
380+
374381
pysqlite_do_all_statements(self, ACTION_FINALIZE, 1);
375382
connection_close(self);
376383

@@ -1258,6 +1265,9 @@ int pysqlite_check_thread(pysqlite_Connection* self)
12581265

12591266
static PyObject* pysqlite_connection_get_isolation_level(pysqlite_Connection* self, void* unused)
12601267
{
1268+
if (!pysqlite_check_connection(self)) {
1269+
return NULL;
1270+
}
12611271
return Py_NewRef(self->isolation_level);
12621272
}
12631273

@@ -1289,11 +1299,17 @@ pysqlite_connection_set_isolation_level(pysqlite_Connection* self, PyObject* iso
12891299
return -1;
12901300
}
12911301
if (isolation_level == Py_None) {
1292-
PyObject *res = pysqlite_connection_commit(self, NULL);
1293-
if (!res) {
1294-
return -1;
1302+
/* We might get called during connection init, so we cannot use
1303+
* pysqlite_connection_commit() here. */
1304+
if (self->db && !sqlite3_get_autocommit(self->db)) {
1305+
int rc;
1306+
Py_BEGIN_ALLOW_THREADS
1307+
rc = sqlite3_exec(self->db, "COMMIT", NULL, NULL, NULL);
1308+
Py_END_ALLOW_THREADS
1309+
if (rc != SQLITE_OK) {
1310+
return _pysqlite_seterror(self->db);
1311+
}
12951312
}
1296-
Py_DECREF(res);
12971313

12981314
self->begin_statement = NULL;
12991315
} else {

0 commit comments

Comments
 (0)