Skip to content

Commit 689605d

Browse files
fix: leak in os.sendfile
1 parent f74cdf8 commit 689605d

3 files changed

Lines changed: 36 additions & 10 deletions

File tree

Lib/test/test_os/test_os.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3878,6 +3878,17 @@ async def test_trailers(self):
38783878
await self.server.wait_closed()
38793879
self.assertEqual(self.server_buffer, b"abcde123456789")
38803880

3881+
@requires_headers_trailers
3882+
async def test_headers_released_on_invalid_trailers(self):
3883+
# Validation errors after iov_setup of headers must still release
3884+
# the exported buffers, otherwise the bytearray cannot be resized.
3885+
header = bytearray(b"header")
3886+
with self.assertRaisesRegex(TypeError,
3887+
r"sendfile\(\) trailers must be a sequence"):
3888+
os.sendfile(self.sockno, self.fileno, 0, 0,
3889+
headers=[header], trailers=object())
3890+
header.append(0)
3891+
38813892
@requires_headers_trailers
38823893
@requires_32b
38833894
async def test_headers_overflow_32bits(self):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix a leak of header buffer exports in :func:`os.sendfile` on macOS and
2+
FreeBSD.

Modules/posixmodule.c

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12523,11 +12523,15 @@ os_sendfile_impl(PyObject *module, int out_fd, int in_fd, PyObject *offobj,
1252312523
#ifndef __APPLE__
1252412524
off_t sbytes;
1252512525
#endif
12526-
Py_buffer *hbuf, *tbuf;
12526+
Py_buffer *hbuf = NULL, *tbuf = NULL;
1252712527
struct sf_hdtr sf;
12528+
int failed = 1;
12529+
int saved_errno = 0;
1252812530

1252912531
sf.headers = NULL;
1253012532
sf.trailers = NULL;
12533+
sf.hdr_cnt = 0;
12534+
sf.trl_cnt = 0;
1253112535

1253212536
if (headers != NULL) {
1253312537
if (!PySequence_Check(headers)) {
@@ -12546,16 +12550,18 @@ os_sendfile_impl(PyObject *module, int out_fd, int in_fd, PyObject *offobj,
1254612550
if (i > 0) {
1254712551
sf.hdr_cnt = (int)i;
1254812552
if (iov_setup(&(sf.headers), &hbuf,
12549-
headers, sf.hdr_cnt, PyBUF_SIMPLE) < 0)
12550-
return NULL;
12553+
headers, sf.hdr_cnt, PyBUF_SIMPLE) < 0) {
12554+
sf.headers = NULL;
12555+
goto cleanup;
12556+
}
1255112557
#ifdef __APPLE__
1255212558
for (i = 0; i < sf.hdr_cnt; i++) {
1255312559
Py_ssize_t blen = sf.headers[i].iov_len;
1255412560
# define OFF_T_MAX 0x7fffffffffffffff
1255512561
if (sbytes >= OFF_T_MAX - blen) {
1255612562
PyErr_SetString(PyExc_OverflowError,
1255712563
"sendfile() header is too large");
12558-
return NULL;
12564+
goto cleanup;
1255912565
}
1256012566
sbytes += blen;
1256112567
}
@@ -12567,25 +12573,28 @@ os_sendfile_impl(PyObject *module, int out_fd, int in_fd, PyObject *offobj,
1256712573
if (!PySequence_Check(trailers)) {
1256812574
PyErr_SetString(PyExc_TypeError,
1256912575
"sendfile() trailers must be a sequence");
12570-
return NULL;
12576+
goto cleanup;
1257112577
} else {
1257212578
Py_ssize_t i = PySequence_Size(trailers);
1257312579
if (i < 0)
12574-
return NULL;
12580+
goto cleanup;
1257512581
if (i > INT_MAX) {
1257612582
PyErr_SetString(PyExc_OverflowError,
1257712583
"sendfile() trailer is too large");
12578-
return NULL;
12584+
goto cleanup;
1257912585
}
1258012586
if (i > 0) {
1258112587
sf.trl_cnt = (int)i;
1258212588
if (iov_setup(&(sf.trailers), &tbuf,
12583-
trailers, sf.trl_cnt, PyBUF_SIMPLE) < 0)
12584-
return NULL;
12589+
trailers, sf.trl_cnt, PyBUF_SIMPLE) < 0) {
12590+
sf.trailers = NULL;
12591+
goto cleanup;
12592+
}
1258512593
}
1258612594
}
1258712595
}
1258812596

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

12601-
int saved_errno = errno;
12610+
saved_errno = errno;
12611+
12612+
cleanup:
1260212613
if (sf.headers != NULL)
1260312614
iov_cleanup(sf.headers, hbuf, sf.hdr_cnt);
1260412615
if (sf.trailers != NULL)
1260512616
iov_cleanup(sf.trailers, tbuf, sf.trl_cnt);
12617+
if (failed)
12618+
return NULL;
1260612619

1260712620
if (ret < 0) {
1260812621
if ((saved_errno == EAGAIN) || (saved_errno == EBUSY)) {

0 commit comments

Comments
 (0)