From: Miss Skeleton (bot) <31488909+miss-islington@users.noreply.github.com> Date: Mon, 26 Oct 2020 05:46:10 +0000 (-0700) Subject: bpo-41662: Fix bugs in binding parameters in sqlite3 (GH-21998) X-Git-Tag: v3.8.7rc1~63 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=a053a7ecfef006b834a9957468fa461dafb332db;p=thirdparty%2FPython%2Fcpython.git bpo-41662: Fix bugs in binding parameters in sqlite3 (GH-21998) * When the parameters argument is a list, correctly handle the case of changing it during iteration. * When the parameters argument is a custom sequence, no longer override an exception raised in ``__len__()``. (cherry picked from commit 0b419b791077414bbc011a412698ebb362b63761) Co-authored-by: Serhiy Storchaka --- diff --git a/Lib/sqlite3/test/dbapi.py b/Lib/sqlite3/test/dbapi.py index 7c259d2af418..2fb07485acf4 100644 --- a/Lib/sqlite3/test/dbapi.py +++ b/Lib/sqlite3/test/dbapi.py @@ -276,7 +276,7 @@ class CursorTests(unittest.TestCase): self.assertEqual(row[0], "foo") def CheckExecuteParamSequence(self): - class L(object): + class L: def __len__(self): return 1 def __getitem__(self, x): @@ -288,6 +288,18 @@ class CursorTests(unittest.TestCase): row = self.cu.fetchone() self.assertEqual(row[0], "foo") + def CheckExecuteParamSequenceBadLen(self): + # Issue41662: Error in __len__() was overridden with ProgrammingError. + class L: + def __len__(self): + 1/0 + def __getitem__(slf, x): + raise AssertionError + + self.cu.execute("insert into test(name) values ('foo')") + with self.assertRaises(ZeroDivisionError): + self.cu.execute("select name from test where name=?", L()) + def CheckExecuteDictMapping(self): self.cu.execute("insert into test(name) values ('foo')") self.cu.execute("select name from test where name=:name", {"name": "foo"}) diff --git a/Lib/sqlite3/test/regression.py b/Lib/sqlite3/test/regression.py index ce97655c66be..206ecd7ac753 100644 --- a/Lib/sqlite3/test/regression.py +++ b/Lib/sqlite3/test/regression.py @@ -133,6 +133,19 @@ class RegressionTests(unittest.TestCase): con.execute("insert into foo(bar) values (5)") con.execute(SELECT) + def CheckBindMutatingList(self): + # Issue41662: Crash when mutate a list of parameters during iteration. + class X: + def __conform__(self, protocol): + parameters.clear() + return "..." + parameters = [X(), 0] + con = sqlite.connect(":memory:",detect_types=sqlite.PARSE_DECLTYPES) + con.execute("create table foo(bar X, baz integer)") + # Should not crash + with self.assertRaises(IndexError): + con.execute("insert into foo(bar, baz) values (?, ?)", parameters) + def CheckErrorMsgDecodeError(self): # When porting the module to Python 3.0, the error message about # decoding errors disappeared. This verifies they're back again. diff --git a/Misc/NEWS.d/next/Library/2020-08-29-16-07-36.bpo-41662.Mn79zh.rst b/Misc/NEWS.d/next/Library/2020-08-29-16-07-36.bpo-41662.Mn79zh.rst new file mode 100644 index 000000000000..0571c2d110be --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-08-29-16-07-36.bpo-41662.Mn79zh.rst @@ -0,0 +1 @@ +Fixed crash when mutate list of parameters during iteration in :mod:`sqlite3`. diff --git a/Misc/NEWS.d/next/Library/2020-08-30-21-38-57.bpo-41662.6e9iZn.rst b/Misc/NEWS.d/next/Library/2020-08-30-21-38-57.bpo-41662.6e9iZn.rst new file mode 100644 index 000000000000..aecb0a1ea4d0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-08-30-21-38-57.bpo-41662.6e9iZn.rst @@ -0,0 +1,2 @@ +No longer override exceptions raised in ``__len__()`` of a sequence of +parameters in :mod:`sqlite3` with :exc:`~sqlite3.ProgrammingError`. diff --git a/Modules/_sqlite/statement.c b/Modules/_sqlite/statement.c index 491294b0209c..70fd95f0fbbf 100644 --- a/Modules/_sqlite/statement.c +++ b/Modules/_sqlite/statement.c @@ -225,6 +225,9 @@ void pysqlite_statement_bind_parameters(pysqlite_Statement* self, PyObject* para num_params = PyList_GET_SIZE(parameters); } else { num_params = PySequence_Size(parameters); + if (num_params == -1) { + return; + } } if (num_params != num_params_needed) { PyErr_Format(pysqlite_ProgrammingError, @@ -236,9 +239,9 @@ void pysqlite_statement_bind_parameters(pysqlite_Statement* self, PyObject* para for (i = 0; i < num_params; i++) { if (PyTuple_CheckExact(parameters)) { current_param = PyTuple_GET_ITEM(parameters, i); - Py_XINCREF(current_param); + Py_INCREF(current_param); } else if (PyList_CheckExact(parameters)) { - current_param = PyList_GET_ITEM(parameters, i); + current_param = PyList_GetItem(parameters, i); Py_XINCREF(current_param); } else { current_param = PySequence_GetItem(parameters, i);