Skip to content

Commit 33fdd01

Browse files
miss-islingtonalonfarajStanFromIreland
authored
[3.14] gh-160773: Fix use-after-free in OSError.__str__ (GH-160822) (#160885)
(cherry picked from commit 7e5227f) Co-authored-by: Alon <alonfaraj@gmail.com> Co-authored-by: Stan Ulbrych <stan@python.org>
1 parent 6ba95b3 commit 33fdd01

3 files changed

Lines changed: 71 additions & 30 deletions

File tree

‎Lib/test/test_exceptions.py‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1789,6 +1789,21 @@ def __init__(self, code, message, filename, filename2):
17891789
exc = LeakingOSError(1, "some message", "filename.py", "filename2.py")
17901790
exc.__init__(2, "another message", "filename3.py", "filename4.py")
17911791

1792+
def test_oserror_str_evil_mutation(self):
1793+
# gh-160773: str(OSError) must not crash when formatting one
1794+
# attribute clears the others.
1795+
class Evil:
1796+
def __repr__(self):
1797+
exc.filename = None
1798+
exc.filename2 = None
1799+
return 'evil'
1800+
1801+
exc = OSError(2, Evil(), Evil())
1802+
self.assertEqual(str(exc), "[Errno 2] evil: evil")
1803+
exc = OSError(2, Evil(), Evil())
1804+
exc.filename2 = Evil()
1805+
self.assertEqual(str(exc), "[Errno 2] evil: evil -> evil")
1806+
17921807
def test_errno_ENOTDIR(self):
17931808
# Issue #12802: "not a directory" errors are ENOTDIR even on Windows
17941809
with self.assertRaises(OSError) as cm:
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix a crash in :func:`str` of an :exc:`OSError` when formatting one of its
2+
attributes modifies the others.

‎Objects/exceptions.c‎

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2282,46 +2282,70 @@ static PyObject *
22822282
OSError_str(PyObject *op)
22832283
{
22842284
PyOSErrorObject *self = PyOSErrorObject_CAST(op);
2285+
PyObject *res;
2286+
/* Hold strong references: formatting may run arbitrary code
2287+
which modifies the attributes. */
2288+
PyObject *myerrno = Py_XNewRef(self->myerrno);
2289+
PyObject *strerror = Py_XNewRef(self->strerror);
2290+
PyObject *filename = Py_XNewRef(self->filename);
2291+
PyObject *filename2 = Py_XNewRef(self->filename2);
2292+
#ifdef MS_WINDOWS
2293+
PyObject *winerror = Py_XNewRef(self->winerror);
2294+
#endif
22852295
#define OR_NONE(x) ((x)?(x):Py_None)
22862296
#ifdef MS_WINDOWS
22872297
/* If available, winerror has the priority over myerrno */
2288-
if (self->winerror && self->filename) {
2289-
if (self->filename2) {
2290-
return PyUnicode_FromFormat("[WinError %S] %S: %R -> %R",
2291-
OR_NONE(self->winerror),
2292-
OR_NONE(self->strerror),
2293-
self->filename,
2294-
self->filename2);
2298+
if (winerror && filename) {
2299+
if (filename2) {
2300+
res = PyUnicode_FromFormat("[WinError %S] %S: %R -> %R",
2301+
OR_NONE(winerror),
2302+
OR_NONE(strerror),
2303+
filename,
2304+
filename2);
22952305
} else {
2296-
return PyUnicode_FromFormat("[WinError %S] %S: %R",
2297-
OR_NONE(self->winerror),
2298-
OR_NONE(self->strerror),
2299-
self->filename);
2306+
res = PyUnicode_FromFormat("[WinError %S] %S: %R",
2307+
OR_NONE(winerror),
2308+
OR_NONE(strerror),
2309+
filename);
23002310
}
2311+
goto done;
2312+
}
2313+
if (winerror && strerror) {
2314+
res = PyUnicode_FromFormat("[WinError %S] %S",
2315+
winerror, strerror);
2316+
goto done;
23012317
}
2302-
if (self->winerror && self->strerror)
2303-
return PyUnicode_FromFormat("[WinError %S] %S",
2304-
self->winerror ? self->winerror: Py_None,
2305-
self->strerror ? self->strerror: Py_None);
23062318
#endif
2307-
if (self->filename) {
2308-
if (self->filename2) {
2309-
return PyUnicode_FromFormat("[Errno %S] %S: %R -> %R",
2310-
OR_NONE(self->myerrno),
2311-
OR_NONE(self->strerror),
2312-
self->filename,
2313-
self->filename2);
2319+
if (filename) {
2320+
if (filename2) {
2321+
res = PyUnicode_FromFormat("[Errno %S] %S: %R -> %R",
2322+
OR_NONE(myerrno),
2323+
OR_NONE(strerror),
2324+
filename,
2325+
filename2);
23142326
} else {
2315-
return PyUnicode_FromFormat("[Errno %S] %S: %R",
2316-
OR_NONE(self->myerrno),
2317-
OR_NONE(self->strerror),
2318-
self->filename);
2327+
res = PyUnicode_FromFormat("[Errno %S] %S: %R",
2328+
OR_NONE(myerrno),
2329+
OR_NONE(strerror),
2330+
filename);
23192331
}
2332+
goto done;
23202333
}
2321-
if (self->myerrno && self->strerror)
2322-
return PyUnicode_FromFormat("[Errno %S] %S",
2323-
self->myerrno, self->strerror);
2324-
return BaseException_str(op);
2334+
if (myerrno && strerror) {
2335+
res = PyUnicode_FromFormat("[Errno %S] %S",
2336+
myerrno, strerror);
2337+
goto done;
2338+
}
2339+
res = BaseException_str(op);
2340+
done:
2341+
Py_XDECREF(myerrno);
2342+
Py_XDECREF(strerror);
2343+
Py_XDECREF(filename);
2344+
Py_XDECREF(filename2);
2345+
#ifdef MS_WINDOWS
2346+
Py_XDECREF(winerror);
2347+
#endif
2348+
return res;
23252349
}
23262350

23272351
static PyObject *

0 commit comments

Comments
 (0)