[3.14] gh-139283: correctly handle size limit in cursor.fetchmany() (GH-139296) (GH-139441)

Passing a negative or zero size to `cursor.fetchmany()` made it fetch all rows
instead of none.

While this could be considered a security vulnerability, it was decided to treat
this issue as a regular bug as passing a non-sanitized *size* value in the first
place is not recommended.
(cherry picked from commit bc172ee830)

Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Co-authored-by: Petr Viktorin <encukou@gmail.com>
This commit is contained in:
Miss Islington (bot) 2025-10-07 21:23:08 +02:00 committed by GitHub
parent cd8fc3aad3
commit cde02ae782
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 134 additions and 19 deletions

View file

@ -6,6 +6,7 @@ preserve
# include "pycore_gc.h" // PyGC_Head
# include "pycore_runtime.h" // _Py_ID()
#endif
#include "pycore_long.h" // _PyLong_UInt32_Converter()
#include "pycore_modsupport.h" // _PyArg_CheckPositional()
static int
@ -181,7 +182,7 @@ PyDoc_STRVAR(pysqlite_cursor_fetchmany__doc__,
{"fetchmany", _PyCFunction_CAST(pysqlite_cursor_fetchmany), METH_FASTCALL|METH_KEYWORDS, pysqlite_cursor_fetchmany__doc__},
static PyObject *
pysqlite_cursor_fetchmany_impl(pysqlite_Cursor *self, int maxrows);
pysqlite_cursor_fetchmany_impl(pysqlite_Cursor *self, uint32_t maxrows);
static PyObject *
pysqlite_cursor_fetchmany(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
@ -216,7 +217,7 @@ pysqlite_cursor_fetchmany(PyObject *self, PyObject *const *args, Py_ssize_t narg
#undef KWTUPLE
PyObject *argsbuf[1];
Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0;
int maxrows = ((pysqlite_Cursor *)self)->arraysize;
uint32_t maxrows = ((pysqlite_Cursor *)self)->arraysize;
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
/*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
@ -226,8 +227,7 @@ pysqlite_cursor_fetchmany(PyObject *self, PyObject *const *args, Py_ssize_t narg
if (!noptargs) {
goto skip_optional_pos;
}
maxrows = PyLong_AsInt(args[0]);
if (maxrows == -1 && PyErr_Occurred()) {
if (!_PyLong_UInt32_Converter(args[0], &maxrows)) {
goto exit;
}
skip_optional_pos:
@ -329,4 +329,46 @@ pysqlite_cursor_close(PyObject *self, PyObject *Py_UNUSED(ignored))
{
return pysqlite_cursor_close_impl((pysqlite_Cursor *)self);
}
/*[clinic end generated code: output=d05c7cbbc8bcab26 input=a9049054013a1b77]*/
#if !defined(_sqlite3_Cursor_arraysize_DOCSTR)
# define _sqlite3_Cursor_arraysize_DOCSTR NULL
#endif
#if defined(_SQLITE3_CURSOR_ARRAYSIZE_GETSETDEF)
# undef _SQLITE3_CURSOR_ARRAYSIZE_GETSETDEF
# define _SQLITE3_CURSOR_ARRAYSIZE_GETSETDEF {"arraysize", (getter)_sqlite3_Cursor_arraysize_get, (setter)_sqlite3_Cursor_arraysize_set, _sqlite3_Cursor_arraysize_DOCSTR},
#else
# define _SQLITE3_CURSOR_ARRAYSIZE_GETSETDEF {"arraysize", (getter)_sqlite3_Cursor_arraysize_get, NULL, _sqlite3_Cursor_arraysize_DOCSTR},
#endif
static PyObject *
_sqlite3_Cursor_arraysize_get_impl(pysqlite_Cursor *self);
static PyObject *
_sqlite3_Cursor_arraysize_get(PyObject *self, void *Py_UNUSED(context))
{
return _sqlite3_Cursor_arraysize_get_impl((pysqlite_Cursor *)self);
}
#if !defined(_sqlite3_Cursor_arraysize_DOCSTR)
# define _sqlite3_Cursor_arraysize_DOCSTR NULL
#endif
#if defined(_SQLITE3_CURSOR_ARRAYSIZE_GETSETDEF)
# undef _SQLITE3_CURSOR_ARRAYSIZE_GETSETDEF
# define _SQLITE3_CURSOR_ARRAYSIZE_GETSETDEF {"arraysize", (getter)_sqlite3_Cursor_arraysize_get, (setter)_sqlite3_Cursor_arraysize_set, _sqlite3_Cursor_arraysize_DOCSTR},
#else
# define _SQLITE3_CURSOR_ARRAYSIZE_GETSETDEF {"arraysize", NULL, (setter)_sqlite3_Cursor_arraysize_set, NULL},
#endif
static int
_sqlite3_Cursor_arraysize_set_impl(pysqlite_Cursor *self, PyObject *value);
static int
_sqlite3_Cursor_arraysize_set(PyObject *self, PyObject *value, void *Py_UNUSED(context))
{
int return_value;
return_value = _sqlite3_Cursor_arraysize_set_impl((pysqlite_Cursor *)self, value);
return return_value;
}
/*[clinic end generated code: output=a0e3ebba9e4d0ece input=a9049054013a1b77]*/

View file

@ -1162,35 +1162,31 @@ pysqlite_cursor_fetchone_impl(pysqlite_Cursor *self)
/*[clinic input]
_sqlite3.Cursor.fetchmany as pysqlite_cursor_fetchmany
size as maxrows: int(c_default='((pysqlite_Cursor *)self)->arraysize') = 1
size as maxrows: uint32(c_default='((pysqlite_Cursor *)self)->arraysize') = 1
The default value is set by the Cursor.arraysize attribute.
Fetches several rows from the resultset.
[clinic start generated code]*/
static PyObject *
pysqlite_cursor_fetchmany_impl(pysqlite_Cursor *self, int maxrows)
/*[clinic end generated code: output=a8ef31fea64d0906 input=035dbe44a1005bf2]*/
pysqlite_cursor_fetchmany_impl(pysqlite_Cursor *self, uint32_t maxrows)
/*[clinic end generated code: output=3325f2b477c71baf input=a509c412aa70b27e]*/
{
PyObject* row;
PyObject* list;
int counter = 0;
list = PyList_New(0);
if (!list) {
return NULL;
}
while ((row = pysqlite_cursor_iternext((PyObject *)self))) {
if (PyList_Append(list, row) < 0) {
Py_DECREF(row);
break;
}
while (maxrows > 0 && (row = pysqlite_cursor_iternext((PyObject *)self))) {
int rc = PyList_Append(list, row);
Py_DECREF(row);
if (++counter == maxrows) {
if (rc < 0) {
break;
}
maxrows--;
}
if (PyErr_Occurred()) {
@ -1304,6 +1300,30 @@ pysqlite_cursor_close_impl(pysqlite_Cursor *self)
Py_RETURN_NONE;
}
/*[clinic input]
@getter
_sqlite3.Cursor.arraysize
[clinic start generated code]*/
static PyObject *
_sqlite3_Cursor_arraysize_get_impl(pysqlite_Cursor *self)
/*[clinic end generated code: output=e0919d97175e6c50 input=3278f8d3ecbd90e3]*/
{
return PyLong_FromUInt32(self->arraysize);
}
/*[clinic input]
@setter
_sqlite3.Cursor.arraysize
[clinic start generated code]*/
static int
_sqlite3_Cursor_arraysize_set_impl(pysqlite_Cursor *self, PyObject *value)
/*[clinic end generated code: output=af59a6b09f8cce6e input=ace48cb114e26060]*/
{
return PyLong_AsUInt32(value, &self->arraysize);
}
static PyMethodDef cursor_methods[] = {
PYSQLITE_CURSOR_CLOSE_METHODDEF
PYSQLITE_CURSOR_EXECUTEMANY_METHODDEF
@ -1321,7 +1341,6 @@ static struct PyMemberDef cursor_members[] =
{
{"connection", _Py_T_OBJECT, offsetof(pysqlite_Cursor, connection), Py_READONLY},
{"description", _Py_T_OBJECT, offsetof(pysqlite_Cursor, description), Py_READONLY},
{"arraysize", Py_T_INT, offsetof(pysqlite_Cursor, arraysize), 0},
{"lastrowid", _Py_T_OBJECT, offsetof(pysqlite_Cursor, lastrowid), Py_READONLY},
{"rowcount", Py_T_LONG, offsetof(pysqlite_Cursor, rowcount), Py_READONLY},
{"row_factory", _Py_T_OBJECT, offsetof(pysqlite_Cursor, row_factory), 0},
@ -1329,6 +1348,11 @@ static struct PyMemberDef cursor_members[] =
{NULL}
};
static struct PyGetSetDef cursor_getsets[] = {
_SQLITE3_CURSOR_ARRAYSIZE_GETSETDEF
{NULL},
};
static const char cursor_doc[] =
PyDoc_STR("SQLite database cursor class.");
@ -1339,6 +1363,7 @@ static PyType_Slot cursor_slots[] = {
{Py_tp_iternext, pysqlite_cursor_iternext},
{Py_tp_methods, cursor_methods},
{Py_tp_members, cursor_members},
{Py_tp_getset, cursor_getsets},
{Py_tp_init, pysqlite_cursor_init},
{Py_tp_traverse, cursor_traverse},
{Py_tp_clear, cursor_clear},

View file

@ -35,7 +35,7 @@ typedef struct
pysqlite_Connection* connection;
PyObject* description;
PyObject* row_cast_map;
int arraysize;
uint32_t arraysize;
PyObject* lastrowid;
long rowcount;
PyObject* row_factory;