gh-140607: Validate returned byte count in RawIOBase.read (#140611)
While `RawIOBase.readinto` should return a count of bytes between 0 and the length of the given buffer, it is not required to. Add validation inside RawIOBase.read() that the returned byte count is valid. Co-authored-by: Shamil <ashm.tech@proton.me> Co-authored-by: Victor Stinner <vstinner@python.org>
This commit is contained in:
@@ -617,6 +617,8 @@ class RawIOBase(IOBase):
|
|||||||
n = self.readinto(b)
|
n = self.readinto(b)
|
||||||
if n is None:
|
if n is None:
|
||||||
return None
|
return None
|
||||||
|
if n < 0 or n > len(b):
|
||||||
|
raise ValueError(f"readinto returned {n} outside buffer size {len(b)}")
|
||||||
del b[n:]
|
del b[n:]
|
||||||
return bytes(b)
|
return bytes(b)
|
||||||
|
|
||||||
|
|||||||
@@ -592,6 +592,22 @@ class IOTest:
|
|||||||
self.assertEqual(rawio.read(2), None)
|
self.assertEqual(rawio.read(2), None)
|
||||||
self.assertEqual(rawio.read(2), b"")
|
self.assertEqual(rawio.read(2), b"")
|
||||||
|
|
||||||
|
def test_RawIOBase_read_bounds_checking(self):
|
||||||
|
# Make sure a `.readinto` call which returns a value outside
|
||||||
|
# (0, len(buffer)) raises.
|
||||||
|
class Misbehaved(self.io.RawIOBase):
|
||||||
|
def __init__(self, readinto_return) -> None:
|
||||||
|
self._readinto_return = readinto_return
|
||||||
|
def readinto(self, b):
|
||||||
|
return self._readinto_return
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError) as cm:
|
||||||
|
Misbehaved(2).read(1)
|
||||||
|
self.assertEqual(str(cm.exception), "readinto returned 2 outside buffer size 1")
|
||||||
|
for bad_size in (2147483647, sys.maxsize, -1, -1000):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
Misbehaved(bad_size).read()
|
||||||
|
|
||||||
def test_types_have_dict(self):
|
def test_types_have_dict(self):
|
||||||
test = (
|
test = (
|
||||||
self.IOBase(),
|
self.IOBase(),
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
Inside :meth:`io.RawIOBase.read`, validate that the count of bytes returned by
|
||||||
|
:meth:`io.RawIOBase.readinto` is valid (inside the provided buffer).
|
||||||
@@ -939,14 +939,21 @@ _io__RawIOBase_read_impl(PyObject *self, Py_ssize_t n)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
n = PyNumber_AsSsize_t(res, PyExc_ValueError);
|
Py_ssize_t bytes_filled = PyNumber_AsSsize_t(res, PyExc_ValueError);
|
||||||
Py_DECREF(res);
|
Py_DECREF(res);
|
||||||
if (n == -1 && PyErr_Occurred()) {
|
if (bytes_filled == -1 && PyErr_Occurred()) {
|
||||||
Py_DECREF(b);
|
Py_DECREF(b);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (bytes_filled < 0 || bytes_filled > n) {
|
||||||
|
Py_DECREF(b);
|
||||||
|
PyErr_Format(PyExc_ValueError,
|
||||||
|
"readinto returned %zd outside buffer size %zd",
|
||||||
|
bytes_filled, n);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
res = PyBytes_FromStringAndSize(PyByteArray_AsString(b), n);
|
res = PyBytes_FromStringAndSize(PyByteArray_AsString(b), bytes_filled);
|
||||||
Py_DECREF(b);
|
Py_DECREF(b);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user