Skip to content

Commit 936d518

Browse files
committed
#10811: Fix recursive usage of cursors. Instead of crashing, raise a ProgrammingError now.
1 parent 83b8c0b commit 936d518

File tree

3 files changed

+43
-10
lines changed

3 files changed

+43
-10
lines changed

Lib/sqlite3/test/regression.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,29 @@ def collation_cb(a, b):
281281
# Lone surrogate cannot be encoded to the default encoding (utf8)
282282
"\uDC80", collation_cb)
283283

284+
def CheckRecursiveCursorUse(self):
285+
"""
286+
http://bugs.python.org/issue10811
287+
288+
Recursively using a cursor, such as when reusing it from a generator led to segfaults.
289+
Now we catch recursive cursor usage and raise a ProgrammingError.
290+
"""
291+
con = sqlite.connect(":memory:")
292+
293+
cur = con.cursor()
294+
cur.execute("create table a (bar)")
295+
cur.execute("create table b (baz)")
296+
297+
def foo():
298+
cur.execute("insert into a (bar) values (?)", (1,))
299+
yield 1
300+
301+
try:
302+
cur.executemany("insert into b (baz) values (?)", ((i,) for i in foo()))
303+
self.fail("should have raised ProgrammingError")
304+
except sqlite.ProgrammingError:
305+
pass
306+
284307
def suite():
285308
regression_suite = unittest.makeSuite(RegressionTests, "Check")
286309
return unittest.TestSuite((regression_suite,))

Modules/_sqlite/cursor.c

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -430,9 +430,14 @@ static int check_cursor(pysqlite_Cursor* cur)
430430
if (cur->closed) {
431431
PyErr_SetString(pysqlite_ProgrammingError, "Cannot operate on a closed cursor.");
432432
return 0;
433-
} else {
434-
return pysqlite_check_thread(cur->connection) && pysqlite_check_connection(cur->connection);
435433
}
434+
435+
if (cur->locked) {
436+
PyErr_SetString(pysqlite_ProgrammingError, "Recursive use of cursors not allowed.");
437+
return 0;
438+
}
439+
440+
return pysqlite_check_thread(cur->connection) && pysqlite_check_connection(cur->connection);
436441
}
437442

438443
PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* args)
@@ -455,9 +460,10 @@ PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject*
455460
int allow_8bit_chars;
456461

457462
if (!check_cursor(self)) {
458-
return NULL;
463+
goto error;
459464
}
460465

466+
self->locked = 1;
461467
self->reset = 0;
462468

463469
/* Make shooting yourself in the foot with not utf-8 decodable 8-bit-strings harder */
@@ -470,12 +476,12 @@ PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject*
470476
if (multiple) {
471477
/* executemany() */
472478
if (!PyArg_ParseTuple(args, "OO", &operation, &second_argument)) {
473-
return NULL;
479+
goto error;
474480
}
475481

476482
if (!PyUnicode_Check(operation)) {
477483
PyErr_SetString(PyExc_ValueError, "operation parameter must be str");
478-
return NULL;
484+
goto error;
479485
}
480486

481487
if (PyIter_Check(second_argument)) {
@@ -486,23 +492,23 @@ PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject*
486492
/* sequence */
487493
parameters_iter = PyObject_GetIter(second_argument);
488494
if (!parameters_iter) {
489-
return NULL;
495+
goto error;
490496
}
491497
}
492498
} else {
493499
/* execute() */
494500
if (!PyArg_ParseTuple(args, "O|O", &operation, &second_argument)) {
495-
return NULL;
501+
goto error;
496502
}
497503

498504
if (!PyUnicode_Check(operation)) {
499505
PyErr_SetString(PyExc_ValueError, "operation parameter must be str");
500-
return NULL;
506+
goto error;
501507
}
502508

503509
parameters_list = PyList_New(0);
504510
if (!parameters_list) {
505-
return NULL;
511+
goto error;
506512
}
507513

508514
if (second_argument == NULL) {
@@ -742,14 +748,17 @@ PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject*
742748
* ROLLBACK could have happened */
743749
#ifdef SQLITE_VERSION_NUMBER
744750
#if SQLITE_VERSION_NUMBER >= 3002002
745-
self->connection->inTransaction = !sqlite3_get_autocommit(self->connection->db);
751+
if (self->connection && self->connection->db)
752+
self->connection->inTransaction = !sqlite3_get_autocommit(self->connection->db);
746753
#endif
747754
#endif
748755

749756
Py_XDECREF(parameters);
750757
Py_XDECREF(parameters_iter);
751758
Py_XDECREF(parameters_list);
752759

760+
self->locked = 0;
761+
753762
if (PyErr_Occurred()) {
754763
self->rowcount = -1L;
755764
return NULL;

Modules/_sqlite/cursor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ typedef struct
4242
pysqlite_Statement* statement;
4343
int closed;
4444
int reset;
45+
int locked;
4546
int initialized;
4647

4748
/* the next row to be returned, NULL if no next row available */

0 commit comments

Comments
 (0)