Skip to content

Commit d6f9a30

Browse files
committed
gh-160773: Fix use-after-free in OSError.__str__
1 parent c71bf4d commit d6f9a30

3 files changed

Lines changed: 71 additions & 30 deletions

File tree

‎Lib/test/test_exceptions.py‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1804,6 +1804,20 @@ def __init__(self, code, message, filename, filename2):
18041804
exc = LeakingOSError(1, "some message", "filename.py", "filename2.py")
18051805
exc.__init__(2, "another message", "filename3.py", "filename4.py")
18061806

1807+
def test_oserror_str_evil_mutation(self):
1808+
# gh-160773: str(OSError) must not crash when formatting one
1809+
# attribute clears the others.
1810+
class Evil:
1811+
def __repr__(self):
1812+
exc.filename = None
1813+
exc.filename2 = None
1814+
return 'evil'
1815+
1816+
exc = OSError(2, Evil(), Evil())
1817+
self.assertEqual(str(exc), "[Errno 2] evil: evil")
1818+
exc = OSError(2, Evil(), Evil(), None, Evil())
1819+
self.assertEqual(str(exc), "[Errno 2] evil: evil -> evil")
1820+
18071821
def test_errno_ENOTDIR(self):
18081822
# Issue #12802: "not a directory" errors are ENOTDIR even on Windows
18091823
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: 55 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2325,46 +2325,71 @@ static PyObject *
23252325
OSError_str(PyObject *op)
23262326
{
23272327
PyOSErrorObject *self = PyOSErrorObject_CAST(op);
2328+
PyObject *res;
2329+
/* Hold strong references: formatting may run arbitrary code
2330+
which modifies the attributes. */
2331+
PyObject *myerrno = Py_XNewRef(self->myerrno);
2332+
PyObject *strerror = Py_XNewRef(self->strerror);
2333+
PyObject *filename = Py_XNewRef(self->filename);
2334+
PyObject *filename2 = Py_XNewRef(self->filename2);
2335+
#ifdef MS_WINDOWS
2336+
PyObject *winerror = Py_XNewRef(self->winerror);
2337+
#endif
23282338
#define OR_NONE(x) ((x)?(x):Py_None)
23292339
#ifdef MS_WINDOWS
23302340
/* If available, winerror has the priority over myerrno */
2331-
if (self->winerror && self->filename) {
2332-
if (self->filename2) {
2333-
return PyUnicode_FromFormat("[WinError %S] %S: %R -> %R",
2334-
OR_NONE(self->winerror),
2335-
OR_NONE(self->strerror),
2336-
self->filename,
2337-
self->filename2);
2341+
if (winerror && filename) {
2342+
if (filename2) {
2343+
res = PyUnicode_FromFormat("[WinError %S] %S: %R -> %R",
2344+
OR_NONE(winerror),
2345+
OR_NONE(strerror),
2346+
filename,
2347+
filename2);
23382348
} else {
2339-
return PyUnicode_FromFormat("[WinError %S] %S: %R",
2340-
OR_NONE(self->winerror),
2341-
OR_NONE(self->strerror),
2342-
self->filename);
2349+
res = PyUnicode_FromFormat("[WinError %S] %S: %R",
2350+
OR_NONE(winerror),
2351+
OR_NONE(strerror),
2352+
filename);
23432353
}
2354+
goto done;
2355+
}
2356+
if (winerror && strerror) {
2357+
res = PyUnicode_FromFormat("[WinError %S] %S",
2358+
winerror ? winerror: Py_None,
2359+
strerror ? strerror: Py_None);
2360+
goto done;
23442361
}
2345-
if (self->winerror && self->strerror)
2346-
return PyUnicode_FromFormat("[WinError %S] %S",
2347-
self->winerror ? self->winerror: Py_None,
2348-
self->strerror ? self->strerror: Py_None);
23492362
#endif
2350-
if (self->filename) {
2351-
if (self->filename2) {
2352-
return PyUnicode_FromFormat("[Errno %S] %S: %R -> %R",
2353-
OR_NONE(self->myerrno),
2354-
OR_NONE(self->strerror),
2355-
self->filename,
2356-
self->filename2);
2363+
if (filename) {
2364+
if (filename2) {
2365+
res = PyUnicode_FromFormat("[Errno %S] %S: %R -> %R",
2366+
OR_NONE(myerrno),
2367+
OR_NONE(strerror),
2368+
filename,
2369+
filename2);
23572370
} else {
2358-
return PyUnicode_FromFormat("[Errno %S] %S: %R",
2359-
OR_NONE(self->myerrno),
2360-
OR_NONE(self->strerror),
2361-
self->filename);
2371+
res = PyUnicode_FromFormat("[Errno %S] %S: %R",
2372+
OR_NONE(myerrno),
2373+
OR_NONE(strerror),
2374+
filename);
23622375
}
2376+
goto done;
23632377
}
2364-
if (self->myerrno && self->strerror)
2365-
return PyUnicode_FromFormat("[Errno %S] %S",
2366-
self->myerrno, self->strerror);
2367-
return BaseException_str(op);
2378+
if (myerrno && strerror) {
2379+
res = PyUnicode_FromFormat("[Errno %S] %S",
2380+
myerrno, strerror);
2381+
goto done;
2382+
}
2383+
res = BaseException_str(op);
2384+
done:
2385+
Py_XDECREF(myerrno);
2386+
Py_XDECREF(strerror);
2387+
Py_XDECREF(filename);
2388+
Py_XDECREF(filename2);
2389+
#ifdef MS_WINDOWS
2390+
Py_XDECREF(winerror);
2391+
#endif
2392+
return res;
23682393
}
23692394

23702395
static PyObject *

0 commit comments

Comments
 (0)