Skip to content

Commit 7e5227f

Browse files
gh-160773: Fix use-after-free in OSError.__str__ (#160822)
Co-authored-by: Stan Ulbrych <stan@python.org>
1 parent 87c0c35 commit 7e5227f

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
@@ -1804,6 +1804,21 @@ 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())
1819+
exc.filename2 = Evil()
1820+
self.assertEqual(str(exc), "[Errno 2] evil: evil -> evil")
1821+
18071822
def test_errno_ENOTDIR(self):
18081823
# Issue #12802: "not a directory" errors are ENOTDIR even on Windows
18091824
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
@@ -2325,46 +2325,70 @@ 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, strerror);
2359+
goto done;
23442360
}
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);
23492361
#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);
2362+
if (filename) {
2363+
if (filename2) {
2364+
res = PyUnicode_FromFormat("[Errno %S] %S: %R -> %R",
2365+
OR_NONE(myerrno),
2366+
OR_NONE(strerror),
2367+
filename,
2368+
filename2);
23572369
} else {
2358-
return PyUnicode_FromFormat("[Errno %S] %S: %R",
2359-
OR_NONE(self->myerrno),
2360-
OR_NONE(self->strerror),
2361-
self->filename);
2370+
res = PyUnicode_FromFormat("[Errno %S] %S: %R",
2371+
OR_NONE(myerrno),
2372+
OR_NONE(strerror),
2373+
filename);
23622374
}
2375+
goto done;
23632376
}
2364-
if (self->myerrno && self->strerror)
2365-
return PyUnicode_FromFormat("[Errno %S] %S",
2366-
self->myerrno, self->strerror);
2367-
return BaseException_str(op);
2377+
if (myerrno && strerror) {
2378+
res = PyUnicode_FromFormat("[Errno %S] %S",
2379+
myerrno, strerror);
2380+
goto done;
2381+
}
2382+
res = BaseException_str(op);
2383+
done:
2384+
Py_XDECREF(myerrno);
2385+
Py_XDECREF(strerror);
2386+
Py_XDECREF(filename);
2387+
Py_XDECREF(filename2);
2388+
#ifdef MS_WINDOWS
2389+
Py_XDECREF(winerror);
2390+
#endif
2391+
return res;
23682392
}
23692393

23702394
static PyObject *

0 commit comments

Comments
 (0)