Skip to content
Snippets Groups Projects
Unverified Commit 2a353b22 authored by Erlend Egeberg Aasland's avatar Erlend Egeberg Aasland Committed by GitHub
Browse files

[3.7] gh-80254: Disallow recursive usage of cursors in sqlite3 converters (GH-92334)


(cherry picked from commit c908dc5b4798c311981bd7e1f7d92fb623ee448b)

Co-authored-by: default avatarSergey Fedoseev <fedoseev.sergey@gmail.com>
Co-authored-by: default avatarJelle Zijlstra <jelle.zijlstra@gmail.com>
parent aebbd757
No related branches found
No related tags found
No related merge requests found
...@@ -27,6 +27,9 @@ ...@@ -27,6 +27,9 @@
import weakref import weakref
from test import support from test import support
from unittest.mock import patch
class RegressionTests(unittest.TestCase): class RegressionTests(unittest.TestCase):
def setUp(self): def setUp(self):
self.con = sqlite.connect(":memory:") self.con = sqlite.connect(":memory:")
...@@ -444,11 +447,50 @@ class UnhashableType(type): ...@@ -444,11 +447,50 @@ class UnhashableType(type):
self.con.execute('SELECT %s()' % aggr_name) self.con.execute('SELECT %s()' % aggr_name)
class RecursiveUseOfCursors(unittest.TestCase):
# GH-80254: sqlite3 should not segfault for recursive use of cursors.
msg = "Recursive use of cursors not allowed"
def setUp(self):
self.con = sqlite.connect(":memory:",
detect_types=sqlite.PARSE_COLNAMES)
self.cur = self.con.cursor()
self.cur.execute("create table test(x foo)")
self.cur.executemany("insert into test(x) values (?)",
[("foo",), ("bar",)])
def tearDown(self):
self.cur.close()
self.con.close()
del self.cur
del self.con
def test_recursive_cursor_init(self):
conv = lambda x: self.cur.__init__(self.con)
with patch.dict(sqlite.converters, {"INIT": conv}):
with self.assertRaisesRegex(sqlite.ProgrammingError, self.msg):
self.cur.execute(f'select x as "x [INIT]", x from test')
def test_recursive_cursor_close(self):
conv = lambda x: self.cur.close()
with patch.dict(sqlite.converters, {"CLOSE": conv}):
with self.assertRaisesRegex(sqlite.ProgrammingError, self.msg):
self.cur.execute(f'select x as "x [CLOSE]", x from test')
def test_recursive_cursor_fetch(self):
conv = lambda x, l=[]: self.cur.fetchone() if l else l.append(None)
with patch.dict(sqlite.converters, {"ITER": conv}):
self.cur.execute(f'select x as "x [ITER]", x from test')
with self.assertRaisesRegex(sqlite.ProgrammingError, self.msg):
self.cur.fetchall()
def suite(): def suite():
regression_suite = unittest.makeSuite(RegressionTests, "Check") regression_suite = unittest.makeSuite(RegressionTests, "Check")
return unittest.TestSuite(( return unittest.TestSuite((
regression_suite, regression_suite,
unittest.makeSuite(UnhashableCallbacksTestCase), unittest.makeSuite(UnhashableCallbacksTestCase),
unittest.makeSuite(RecursiveUseOfCursors),
)) ))
def test(): def test():
......
Raise :exc:`~sqlite3.ProgrammingError` instead of segfaulting on recursive
usage of cursors in :mod:`sqlite3` converters. Patch by Sergey Fedoseev.
...@@ -27,10 +27,25 @@ ...@@ -27,10 +27,25 @@
PyObject* pysqlite_cursor_iternext(pysqlite_Cursor* self); PyObject* pysqlite_cursor_iternext(pysqlite_Cursor* self);
static inline int
check_cursor_locked(pysqlite_Cursor *cur)
{
if (cur->locked) {
PyErr_SetString(pysqlite_ProgrammingError,
"Recursive use of cursors not allowed.");
return 0;
}
return 1;
}
static const char errmsg_fetch_across_rollback[] = "Cursor needed to be reset because of commit/rollback and can no longer be fetched from."; static const char errmsg_fetch_across_rollback[] = "Cursor needed to be reset because of commit/rollback and can no longer be fetched from.";
static int pysqlite_cursor_init(pysqlite_Cursor* self, PyObject* args, PyObject* kwargs) static int pysqlite_cursor_init(pysqlite_Cursor* self, PyObject* args, PyObject* kwargs)
{ {
if (!check_cursor_locked(self)) {
return -1;
}
pysqlite_Connection* connection; pysqlite_Connection* connection;
if (!PyArg_ParseTuple(args, "O!", &pysqlite_ConnectionType, &connection)) if (!PyArg_ParseTuple(args, "O!", &pysqlite_ConnectionType, &connection))
...@@ -376,12 +391,9 @@ static int check_cursor(pysqlite_Cursor* cur) ...@@ -376,12 +391,9 @@ static int check_cursor(pysqlite_Cursor* cur)
return 0; return 0;
} }
if (cur->locked) { return (pysqlite_check_thread(cur->connection)
PyErr_SetString(pysqlite_ProgrammingError, "Recursive use of cursors not allowed."); && pysqlite_check_connection(cur->connection)
return 0; && check_cursor_locked(cur));
}
return pysqlite_check_thread(cur->connection) && pysqlite_check_connection(cur->connection);
} }
PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* args) PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* args)
...@@ -790,27 +802,29 @@ PyObject* pysqlite_cursor_iternext(pysqlite_Cursor *self) ...@@ -790,27 +802,29 @@ PyObject* pysqlite_cursor_iternext(pysqlite_Cursor *self)
if (self->statement) { if (self->statement) {
rc = pysqlite_step(self->statement->st, self->connection); rc = pysqlite_step(self->statement->st, self->connection);
if (PyErr_Occurred()) { if (PyErr_Occurred()) {
(void)pysqlite_statement_reset(self->statement); goto error;
Py_DECREF(next_row);
return NULL;
} }
if (rc != SQLITE_DONE && rc != SQLITE_ROW) { if (rc != SQLITE_DONE && rc != SQLITE_ROW) {
(void)pysqlite_statement_reset(self->statement);
Py_DECREF(next_row);
_pysqlite_seterror(self->connection->db, NULL); _pysqlite_seterror(self->connection->db, NULL);
return NULL; goto error;
} }
if (rc == SQLITE_ROW) { if (rc == SQLITE_ROW) {
self->locked = 1; // GH-80254: Prevent recursive use of cursors.
self->next_row = _pysqlite_fetch_one_row(self); self->next_row = _pysqlite_fetch_one_row(self);
self->locked = 0;
if (self->next_row == NULL) { if (self->next_row == NULL) {
(void)pysqlite_statement_reset(self->statement); goto error;
return NULL;
} }
} }
} }
return next_row; return next_row;
error:
(void)pysqlite_statement_reset(self->statement);
Py_DECREF(next_row);
return NULL;
} }
PyObject* pysqlite_cursor_fetchone(pysqlite_Cursor* self, PyObject* args) PyObject* pysqlite_cursor_fetchone(pysqlite_Cursor* self, PyObject* args)
...@@ -905,6 +919,10 @@ PyObject* pysqlite_noop(pysqlite_Connection* self, PyObject* args) ...@@ -905,6 +919,10 @@ PyObject* pysqlite_noop(pysqlite_Connection* self, PyObject* args)
PyObject* pysqlite_cursor_close(pysqlite_Cursor* self, PyObject* args) PyObject* pysqlite_cursor_close(pysqlite_Cursor* self, PyObject* args)
{ {
if (!check_cursor_locked(self)) {
return NULL;
}
if (!self->connection) { if (!self->connection) {
PyErr_SetString(pysqlite_ProgrammingError, PyErr_SetString(pysqlite_ProgrammingError,
"Base Cursor.__init__ not called."); "Base Cursor.__init__ not called.");
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment