Skip to content
Open
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
11 changes: 11 additions & 0 deletions Lib/test/test_os/test_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -3878,6 +3878,17 @@ async def test_trailers(self):
await self.server.wait_closed()
self.assertEqual(self.server_buffer, b"abcde123456789")

@requires_headers_trailers
async def test_headers_released_on_invalid_trailers(self):
# Validation errors after iov_setup of headers must still release
# the exported buffers, otherwise the bytearray cannot be resized.
header = bytearray(b"header")
with self.assertRaisesRegex(TypeError,
r"sendfile\(\) trailers must be a sequence"):
os.sendfile(self.sockno, self.fileno, 0, 0,
headers=[header], trailers=object())
header.append(0)

@requires_headers_trailers
@requires_32b
async def test_headers_overflow_32bits(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix a leak of header buffer exports in :func:`os.sendfile` on macOS and
FreeBSD.
33 changes: 23 additions & 10 deletions Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -12523,11 +12523,15 @@ os_sendfile_impl(PyObject *module, int out_fd, int in_fd, PyObject *offobj,
#ifndef __APPLE__
off_t sbytes;
#endif
Py_buffer *hbuf, *tbuf;
Py_buffer *hbuf = NULL, *tbuf = NULL;
struct sf_hdtr sf;
int failed = 1;
int saved_errno = 0;

sf.headers = NULL;
sf.trailers = NULL;
sf.hdr_cnt = 0;
sf.trl_cnt = 0;

if (headers != NULL) {
if (!PySequence_Check(headers)) {
Expand All @@ -12546,16 +12550,18 @@ os_sendfile_impl(PyObject *module, int out_fd, int in_fd, PyObject *offobj,
if (i > 0) {
sf.hdr_cnt = (int)i;
if (iov_setup(&(sf.headers), &hbuf,
headers, sf.hdr_cnt, PyBUF_SIMPLE) < 0)
return NULL;
headers, sf.hdr_cnt, PyBUF_SIMPLE) < 0) {
sf.headers = NULL;
goto cleanup;
}
#ifdef __APPLE__
for (i = 0; i < sf.hdr_cnt; i++) {
Py_ssize_t blen = sf.headers[i].iov_len;
# define OFF_T_MAX 0x7fffffffffffffff
if (sbytes >= OFF_T_MAX - blen) {
PyErr_SetString(PyExc_OverflowError,
"sendfile() header is too large");
return NULL;
goto cleanup;
}
sbytes += blen;
}
Expand All @@ -12567,25 +12573,28 @@ os_sendfile_impl(PyObject *module, int out_fd, int in_fd, PyObject *offobj,
if (!PySequence_Check(trailers)) {
PyErr_SetString(PyExc_TypeError,
"sendfile() trailers must be a sequence");
return NULL;
goto cleanup;
} else {
Py_ssize_t i = PySequence_Size(trailers);
if (i < 0)
return NULL;
goto cleanup;
if (i > INT_MAX) {
PyErr_SetString(PyExc_OverflowError,
"sendfile() trailer is too large");
return NULL;
goto cleanup;
}
if (i > 0) {
sf.trl_cnt = (int)i;
if (iov_setup(&(sf.trailers), &tbuf,
trailers, sf.trl_cnt, PyBUF_SIMPLE) < 0)
return NULL;
trailers, sf.trl_cnt, PyBUF_SIMPLE) < 0) {
sf.trailers = NULL;
goto cleanup;
}
}
}
}

failed = 0;
_Py_BEGIN_SUPPRESS_IPH
do {
Py_BEGIN_ALLOW_THREADS
Expand All @@ -12598,11 +12607,15 @@ os_sendfile_impl(PyObject *module, int out_fd, int in_fd, PyObject *offobj,
} while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
_Py_END_SUPPRESS_IPH

int saved_errno = errno;
saved_errno = errno;

cleanup:
if (sf.headers != NULL)
iov_cleanup(sf.headers, hbuf, sf.hdr_cnt);
if (sf.trailers != NULL)
iov_cleanup(sf.trailers, tbuf, sf.trl_cnt);
if (failed)
return NULL;

if (ret < 0) {
if ((saved_errno == EAGAIN) || (saved_errno == EBUSY)) {
Expand Down
Loading