mirror of
https://github.com/python/cpython.git
synced 2025-12-08 06:10:17 +00:00
[3.10] gh-80254: Disallow recursive usage of cursors in sqlite3 converters (#92274)
* [3.10] gh-80254: Disallow recursive usage of cursors in `sqlite3` converters (#29054)
(cherry picked from commit f629dcfe83)
Co-authored-by: Sergey Fedoseev <fedoseev.sergey@gmail.com>
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
* Fix ref leak in pysqlite_cursor_iternext
This commit is contained in:
parent
9b47252d54
commit
2a2421e538
3 changed files with 75 additions and 15 deletions
|
|
@ -27,6 +27,9 @@
|
||||||
import functools
|
import functools
|
||||||
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:")
|
||||||
|
|
@ -415,9 +418,46 @@ def test_return_empty_bytestring(self):
|
||||||
self.assertEqual(val, b'')
|
self.assertEqual(val, b'')
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
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():
|
||||||
tests = [
|
tests = [
|
||||||
RegressionTests
|
RegressionTests,
|
||||||
|
RecursiveUseOfCursors,
|
||||||
]
|
]
|
||||||
return unittest.TestSuite(
|
return unittest.TestSuite(
|
||||||
[unittest.TestLoader().loadTestsFromTestCase(t) for t in tests]
|
[unittest.TestLoader().loadTestsFromTestCase(t) for t in tests]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
Raise :exc:`~sqlite3.ProgrammingError` instead of segfaulting on recursive
|
||||||
|
usage of cursors in :mod:`sqlite3` converters. Patch by Sergey Fedoseev.
|
||||||
|
|
@ -26,6 +26,17 @@
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "clinic/cursor.c.h"
|
#include "clinic/cursor.c.h"
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
module _sqlite3
|
module _sqlite3
|
||||||
class _sqlite3.Cursor "pysqlite_Cursor *" "pysqlite_CursorType"
|
class _sqlite3.Cursor "pysqlite_Cursor *" "pysqlite_CursorType"
|
||||||
|
|
@ -47,6 +58,10 @@ pysqlite_cursor_init_impl(pysqlite_Cursor *self,
|
||||||
pysqlite_Connection *connection)
|
pysqlite_Connection *connection)
|
||||||
/*[clinic end generated code: output=ac59dce49a809ca8 input=a8a4f75ac90999b2]*/
|
/*[clinic end generated code: output=ac59dce49a809ca8 input=a8a4f75ac90999b2]*/
|
||||||
{
|
{
|
||||||
|
if (!check_cursor_locked(self)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
Py_INCREF(connection);
|
Py_INCREF(connection);
|
||||||
Py_XSETREF(self->connection, connection);
|
Py_XSETREF(self->connection, connection);
|
||||||
Py_CLEAR(self->statement);
|
Py_CLEAR(self->statement);
|
||||||
|
|
@ -407,12 +422,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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
|
@ -810,27 +822,29 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
|
|
@ -973,6 +987,10 @@ static PyObject *
|
||||||
pysqlite_cursor_close_impl(pysqlite_Cursor *self)
|
pysqlite_cursor_close_impl(pysqlite_Cursor *self)
|
||||||
/*[clinic end generated code: output=b6055e4ec6fe63b6 input=08b36552dbb9a986]*/
|
/*[clinic end generated code: output=b6055e4ec6fe63b6 input=08b36552dbb9a986]*/
|
||||||
{
|
{
|
||||||
|
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.");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue