Skip to content

bpo-30061: Check if PyObject_Size()/PySequence_Size()/PyMapping_Size() (#1096) #1180

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions Lib/test/test_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,22 @@ def test_readline(self):
with self.open(support.TESTFN, "r") as f:
self.assertRaises(TypeError, f.readline, 5.3)

def test_readline_nonsizeable(self):
# Issue #30061
# Crash when readline() returns an object without __len__
class R(self.IOBase):
def readline(self):
return None
self.assertRaises((TypeError, StopIteration), next, R())

def test_next_nonsizeable(self):
# Issue #30061
# Crash when __next__() returns an object without __len__
class R(self.IOBase):
def __next__(self):
return None
self.assertRaises(TypeError, R().readlines, 1)

def test_raw_bytes_io(self):
f = self.BytesIO()
self.write_ops(f)
Expand Down
5 changes: 5 additions & 0 deletions Misc/NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ Core and Builtins
Library
-------

- bpo-30061: Fixed crashes in IOBase methods __next__() and readlines() when
readline() or __next__() respectively return non-sizeable object.
Fixed possible other errors caused by not checking results of PyObject_Size(),
PySequence_Size(), or PyMapping_Size().

- bpo-30017: Allowed calling the close() method of the zip entry writer object
multiple times. Writing to a closed writer now always produces a ValueError.

Expand Down
13 changes: 9 additions & 4 deletions Modules/_io/iobase.c
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,8 @@ iobase_iternext(PyObject *self)
if (line == NULL)
return NULL;

if (PyObject_Size(line) == 0) {
if (PyObject_Size(line) <= 0) {
/* Error or empty */
Py_DECREF(line);
return NULL;
}
Expand Down Expand Up @@ -676,6 +677,7 @@ _io__IOBase_readlines_impl(PyObject *self, Py_ssize_t hint)
}

while (1) {
Py_ssize_t line_length;
PyObject *line = PyIter_Next(it);
if (line == NULL) {
if (PyErr_Occurred()) {
Expand All @@ -689,11 +691,14 @@ _io__IOBase_readlines_impl(PyObject *self, Py_ssize_t hint)
Py_DECREF(line);
goto error;
}
length += PyObject_Size(line);
line_length = PyObject_Size(line);
Py_DECREF(line);

if (length > hint)
if (line_length < 0) {
goto error;
}
if (line_length > hint - length)
break;
length += line_length;
}

Py_DECREF(it);
Expand Down
17 changes: 11 additions & 6 deletions Modules/_winapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -722,17 +722,22 @@ getenvironment(PyObject* environment)
return NULL;
}

envsize = PyMapping_Length(environment);

keys = PyMapping_Keys(environment);
values = PyMapping_Values(environment);
if (!keys || !values)
goto error;

envsize = PySequence_Fast_GET_SIZE(keys);
if (PySequence_Fast_GET_SIZE(values) != envsize) {
PyErr_SetString(PyExc_RuntimeError,
"environment changed size during iteration");
goto error;
}

totalsize = 1; /* trailing null character */
for (i = 0; i < envsize; i++) {
PyObject* key = PyList_GET_ITEM(keys, i);
PyObject* value = PyList_GET_ITEM(values, i);
PyObject* key = PySequence_Fast_GET_ITEM(keys, i);
PyObject* value = PySequence_Fast_GET_ITEM(values, i);

if (! PyUnicode_Check(key) || ! PyUnicode_Check(value)) {
PyErr_SetString(PyExc_TypeError,
Expand Down Expand Up @@ -760,8 +765,8 @@ getenvironment(PyObject* environment)
end = buffer + totalsize;

for (i = 0; i < envsize; i++) {
PyObject* key = PyList_GET_ITEM(keys, i);
PyObject* value = PyList_GET_ITEM(values, i);
PyObject* key = PySequence_Fast_GET_ITEM(keys, i);
PyObject* value = PySequence_Fast_GET_ITEM(values, i);
if (!PyUnicode_AsUCS4(key, p, end - p, 0))
goto error;
p += PyUnicode_GET_LENGTH(key);
Expand Down
3 changes: 3 additions & 0 deletions Modules/cjkcodecs/multibytecodec.c
Original file line number Diff line number Diff line change
Expand Up @@ -1670,6 +1670,9 @@ _multibytecodec_MultibyteStreamWriter_writelines(MultibyteStreamWriterObject *se
if (r == -1)
return NULL;
}
/* PySequence_Length() can fail */
if (PyErr_Occurred())
return NULL;

Py_RETURN_NONE;
}
Expand Down
60 changes: 42 additions & 18 deletions Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -6650,14 +6650,17 @@ static PyObject *
os_setgroups(PyObject *module, PyObject *groups)
/*[clinic end generated code: output=3fcb32aad58c5ecd input=fa742ca3daf85a7e]*/
{
int i, len;
Py_ssize_t i, len;
gid_t grouplist[MAX_GROUPS];

if (!PySequence_Check(groups)) {
PyErr_SetString(PyExc_TypeError, "setgroups argument must be a sequence");
return NULL;
}
len = PySequence_Size(groups);
if (len < 0) {
return NULL;
}
if (len > MAX_GROUPS) {
PyErr_SetString(PyExc_ValueError, "too many groups");
return NULL;
Expand Down Expand Up @@ -7886,9 +7889,9 @@ os_read_impl(PyObject *module, int fd, Py_ssize_t length)
#if (defined(HAVE_SENDFILE) && (defined(__FreeBSD__) || defined(__DragonFly__) \
|| defined(__APPLE__))) || defined(HAVE_READV) || defined(HAVE_WRITEV)
static Py_ssize_t
iov_setup(struct iovec **iov, Py_buffer **buf, PyObject *seq, int cnt, int type)
iov_setup(struct iovec **iov, Py_buffer **buf, PyObject *seq, Py_ssize_t cnt, int type)
{
int i, j;
Py_ssize_t i, j;
Py_ssize_t blen, total = 0;

*iov = PyMem_New(struct iovec, cnt);
Expand Down Expand Up @@ -7965,8 +7968,7 @@ static Py_ssize_t
os_readv_impl(PyObject *module, int fd, PyObject *buffers)
/*[clinic end generated code: output=792da062d3fcebdb input=e679eb5dbfa0357d]*/
{
int cnt;
Py_ssize_t n;
Py_ssize_t cnt, n;
int async_err = 0;
struct iovec *iov;
Py_buffer *buf;
Expand All @@ -7978,6 +7980,8 @@ os_readv_impl(PyObject *module, int fd, PyObject *buffers)
}

cnt = PySequence_Size(buffers);
if (cnt < 0)
return -1;

if (iov_setup(&iov, &buf, buffers, cnt, PyBUF_WRITABLE) < 0)
return -1;
Expand Down Expand Up @@ -8116,15 +8120,24 @@ posix_sendfile(PyObject *self, PyObject *args, PyObject *kwdict)
"sendfile() headers must be a sequence");
return NULL;
} else {
Py_ssize_t i = 0; /* Avoid uninitialized warning */
sf.hdr_cnt = PySequence_Size(headers);
if (sf.hdr_cnt > 0 &&
(i = iov_setup(&(sf.headers), &hbuf,
headers, sf.hdr_cnt, PyBUF_SIMPLE)) < 0)
Py_ssize_t i = PySequence_Size(headers);
if (i < 0)
return NULL;
if (i > INT_MAX) {
PyErr_SetString(PyExc_OverflowError,
"sendfile() header is too large");
return NULL;
}
if (i > 0) {
sf.hdr_cnt = (int)i;
i = iov_setup(&(sf.headers), &hbuf,
headers, sf.hdr_cnt, PyBUF_SIMPLE);
if (i < 0)
return NULL;
#ifdef __APPLE__
sbytes += i;
sbytes += i;
#endif
}
}
}
if (trailers != NULL) {
Expand All @@ -8133,15 +8146,24 @@ posix_sendfile(PyObject *self, PyObject *args, PyObject *kwdict)
"sendfile() trailers must be a sequence");
return NULL;
} else {
Py_ssize_t i = 0; /* Avoid uninitialized warning */
sf.trl_cnt = PySequence_Size(trailers);
if (sf.trl_cnt > 0 &&
(i = iov_setup(&(sf.trailers), &tbuf,
trailers, sf.trl_cnt, PyBUF_SIMPLE)) < 0)
Py_ssize_t i = PySequence_Size(trailers);
if (i < 0)
return NULL;
if (i > INT_MAX) {
PyErr_SetString(PyExc_OverflowError,
"sendfile() trailer is too large");
return NULL;
}
if (i > 0) {
sf.trl_cnt = (int)i;
i = iov_setup(&(sf.trailers), &tbuf,
trailers, sf.trl_cnt, PyBUF_SIMPLE);
if (i < 0)
return NULL;
#ifdef __APPLE__
sbytes += i;
sbytes += i;
#endif
}
}
}

Expand Down Expand Up @@ -8411,7 +8433,7 @@ static Py_ssize_t
os_writev_impl(PyObject *module, int fd, PyObject *buffers)
/*[clinic end generated code: output=56565cfac3aac15b input=5b8d17fe4189d2fe]*/
{
int cnt;
Py_ssize_t cnt;
Py_ssize_t result;
int async_err = 0;
struct iovec *iov;
Expand All @@ -8423,6 +8445,8 @@ os_writev_impl(PyObject *module, int fd, PyObject *buffers)
return -1;
}
cnt = PySequence_Size(buffers);
if (cnt < 0)
return -1;

if (iov_setup(&iov, &buf, buffers, cnt, PyBUF_SIMPLE) < 0) {
return -1;
Expand Down
12 changes: 9 additions & 3 deletions Objects/setobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1550,20 +1550,26 @@ set_difference(PySetObject *so, PyObject *other)
PyObject *key;
Py_hash_t hash;
setentry *entry;
Py_ssize_t pos = 0;
Py_ssize_t pos = 0, other_size;
int rv;

if (PySet_GET_SIZE(so) == 0) {
return set_copy(so);
}

if (!PyAnySet_Check(other) && !PyDict_CheckExact(other)) {
if (PyAnySet_Check(other)) {
other_size = PySet_GET_SIZE(other);
}
else if (PyDict_CheckExact(other)) {
other_size = PyDict_Size(other);
}
else {
return set_copy_and_difference(so, other);
}

/* If len(so) much more than len(other), it's more efficient to simply copy
* so and then iterate other looking for common elements. */
if ((PySet_GET_SIZE(so) >> 2) > PyObject_Size(other)) {
if ((PySet_GET_SIZE(so) >> 2) > other_size) {
return set_copy_and_difference(so, other);
}

Expand Down