From 4a84f58143ca01db181f28df06ac922fbf73a1d7 Mon Sep 17 00:00:00 2001 From: Petri Lehtinen Date: Mon, 9 May 2011 12:24:09 +0200 Subject: [PATCH] Issue #10811: Fix recursive usage of cursors. Instead of crashing, raise a ProgrammingError now. --- Lib/sqlite3/test/regression.py | 22 ++++++++++++++++++++++ Misc/NEWS | 3 +++ Modules/_sqlite/cursor.c | 29 +++++++++++++++++++---------- Modules/_sqlite/cursor.h | 1 + 4 files changed, 45 insertions(+), 10 deletions(-) diff --git a/Lib/sqlite3/test/regression.py b/Lib/sqlite3/test/regression.py index 7d0553d8f1f..c7551e35a1b 100644 --- a/Lib/sqlite3/test/regression.py +++ b/Lib/sqlite3/test/regression.py @@ -281,6 +281,28 @@ class RegressionTests(unittest.TestCase): # Lone surrogate cannot be encoded to the default encoding (utf8) "\uDC80", collation_cb) + def CheckRecursiveCursorUse(self): + """ + http://bugs.python.org/issue10811 + + Recursively using a cursor, such as when reusing it from a generator led to segfaults. + Now we catch recursive cursor usage and raise a ProgrammingError. + """ + con = sqlite.connect(":memory:") + + cur = con.cursor() + cur.execute("create table a (bar)") + cur.execute("create table b (baz)") + + def foo(): + cur.execute("insert into a (bar) values (?)", (1,)) + yield 1 + + with self.assertRaises(sqlite.ProgrammingError): + cur.executemany("insert into b (baz) values (?)", + ((i,) for i in foo())) + + def suite(): regression_suite = unittest.makeSuite(RegressionTests, "Check") return unittest.TestSuite((regression_suite,)) diff --git a/Misc/NEWS b/Misc/NEWS index 5761ed0a1bc..4a38b148a0d 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -113,6 +113,9 @@ Core and Builtins Library ------- +- Issue #10811: Fix recursive usage of cursors. Instead of crashing, + raise a ProgrammingError now. + - Issue #10881: Fix test_site failure with OS X framework builds. - Issue #964437 Make IDLE help window non-modal. diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index 763322ff1b6..86fbd4ee0d2 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -433,9 +433,14 @@ static int check_cursor(pysqlite_Cursor* cur) if (cur->closed) { PyErr_SetString(pysqlite_ProgrammingError, "Cannot operate on a closed cursor."); return 0; - } else { - return pysqlite_check_thread(cur->connection) && pysqlite_check_connection(cur->connection); } + + if (cur->locked) { + PyErr_SetString(pysqlite_ProgrammingError, "Recursive use of cursors not allowed."); + return 0; + } + + return pysqlite_check_thread(cur->connection) && pysqlite_check_connection(cur->connection); } PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* args) @@ -458,9 +463,10 @@ PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* int allow_8bit_chars; if (!check_cursor(self)) { - return NULL; + goto error; } + self->locked = 1; self->reset = 0; /* Make shooting yourself in the foot with not utf-8 decodable 8-bit-strings harder */ @@ -473,12 +479,12 @@ PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* if (multiple) { /* executemany() */ if (!PyArg_ParseTuple(args, "OO", &operation, &second_argument)) { - return NULL; + goto error; } if (!PyUnicode_Check(operation)) { PyErr_SetString(PyExc_ValueError, "operation parameter must be str"); - return NULL; + goto error; } if (PyIter_Check(second_argument)) { @@ -489,23 +495,23 @@ PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* /* sequence */ parameters_iter = PyObject_GetIter(second_argument); if (!parameters_iter) { - return NULL; + goto error; } } } else { /* execute() */ if (!PyArg_ParseTuple(args, "O|O", &operation, &second_argument)) { - return NULL; + goto error; } if (!PyUnicode_Check(operation)) { PyErr_SetString(PyExc_ValueError, "operation parameter must be str"); - return NULL; + goto error; } parameters_list = PyList_New(0); if (!parameters_list) { - return NULL; + goto error; } if (second_argument == NULL) { @@ -745,7 +751,8 @@ error: * ROLLBACK could have happened */ #ifdef SQLITE_VERSION_NUMBER #if SQLITE_VERSION_NUMBER >= 3002002 - self->connection->inTransaction = !sqlite3_get_autocommit(self->connection->db); + if (self->connection && self->connection->db) + self->connection->inTransaction = !sqlite3_get_autocommit(self->connection->db); #endif #endif @@ -753,6 +760,8 @@ error: Py_XDECREF(parameters_iter); Py_XDECREF(parameters_list); + self->locked = 0; + if (PyErr_Occurred()) { self->rowcount = -1L; return NULL; diff --git a/Modules/_sqlite/cursor.h b/Modules/_sqlite/cursor.h index 5d8b5c160d5..118ba388a41 100644 --- a/Modules/_sqlite/cursor.h +++ b/Modules/_sqlite/cursor.h @@ -42,6 +42,7 @@ typedef struct pysqlite_Statement* statement; int closed; int reset; + int locked; int initialized; /* the next row to be returned, NULL if no next row available */