Skip to content

Commit 2752a3e

Browse files
miss-islingtonalonfarajStanFromIreland
authored
[3.15] gh-160773: Fix use-after-free in OSError.__str__ (GH-160822) (#160884)
(cherry picked from commit 7e5227f) Co-authored-by: Alon <alonfaraj@gmail.com> Co-authored-by: Stan Ulbrych <stan@python.org>
1 parent 34f9898 commit 2752a3e

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
@@ -1797,6 +1797,21 @@ def __init__(self, code, message, filename, filename2):
17971797
exc = LeakingOSError(1, "some message", "filename.py", "filename2.py")
17981798
exc.__init__(2, "another message", "filename3.py", "filename4.py")
17991799

1800+
def test_oserror_str_evil_mutation(self):
1801+
# gh-160773: str(OSError) must not crash when formatting one
1802+
# attribute clears the others.
1803+
class Evil:
1804+
def __repr__(self):
1805+
exc.filename = None
1806+
exc.filename2 = None
1807+
return 'evil'
1808+
1809+
exc = OSError(2, Evil(), Evil())
1810+
self.assertEqual(str(exc), "[Errno 2] evil: evil")
1811+
exc = OSError(2, Evil(), Evil())
1812+
exc.filename2 = Evil()
1813+
self.assertEqual(str(exc), "[Errno 2] evil: evil -> evil")
1814+
18001815
def test_errno_ENOTDIR(self):
18011816
# Issue #12802: "not a directory" errors are ENOTDIR even on Windows
18021817
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
@@ -2347,46 +2347,70 @@ static PyObject *
23472347
OSError_str(PyObject *op)
23482348
{
23492349
PyOSErrorObject *self = PyOSErrorObject_CAST(op);
2350+
PyObject *res;
2351+
/* Hold strong references: formatting may run arbitrary code
2352+
which modifies the attributes. */
2353+
PyObject *myerrno = Py_XNewRef(self->myerrno);
2354+
PyObject *strerror = Py_XNewRef(self->strerror);
2355+
PyObject *filename = Py_XNewRef(self->filename);
2356+
PyObject *filename2 = Py_XNewRef(self->filename2);
2357+
#ifdef MS_WINDOWS
2358+
PyObject *winerror = Py_XNewRef(self->winerror);
2359+
#endif
23502360
#define OR_NONE(x) ((x)?(x):Py_None)
23512361
#ifdef MS_WINDOWS
23522362
/* If available, winerror has the priority over myerrno */
2353-
if (self->winerror && self->filename) {
2354-
if (self->filename2) {
2355-
return PyUnicode_FromFormat("[WinError %S] %S: %R -> %R",
2356-
OR_NONE(self->winerror),
2357-
OR_NONE(self->strerror),
2358-
self->filename,
2359-
self->filename2);
2363+
if (winerror && filename) {
2364+
if (filename2) {
2365+
res = PyUnicode_FromFormat("[WinError %S] %S: %R -> %R",
2366+
OR_NONE(winerror),
2367+
OR_NONE(strerror),
2368+
filename,
2369+
filename2);
23602370
} else {
2361-
return PyUnicode_FromFormat("[WinError %S] %S: %R",
2362-
OR_NONE(self->winerror),
2363-
OR_NONE(self->strerror),
2364-
self->filename);
2371+
res = PyUnicode_FromFormat("[WinError %S] %S: %R",
2372+
OR_NONE(winerror),
2373+
OR_NONE(strerror),
2374+
filename);
23652375
}
2376+
goto done;
2377+
}
2378+
if (winerror && strerror) {
2379+
res = PyUnicode_FromFormat("[WinError %S] %S",
2380+
winerror, strerror);
2381+
goto done;
23662382
}
2367-
if (self->winerror && self->strerror)
2368-
return PyUnicode_FromFormat("[WinError %S] %S",
2369-
self->winerror ? self->winerror: Py_None,
2370-
self->strerror ? self->strerror: Py_None);
23712383
#endif
2372-
if (self->filename) {
2373-
if (self->filename2) {
2374-
return PyUnicode_FromFormat("[Errno %S] %S: %R -> %R",
2375-
OR_NONE(self->myerrno),
2376-
OR_NONE(self->strerror),
2377-
self->filename,
2378-
self->filename2);
2384+
if (filename) {
2385+
if (filename2) {
2386+
res = PyUnicode_FromFormat("[Errno %S] %S: %R -> %R",
2387+
OR_NONE(myerrno),
2388+
OR_NONE(strerror),
2389+
filename,
2390+
filename2);
23792391
} else {
2380-
return PyUnicode_FromFormat("[Errno %S] %S: %R",
2381-
OR_NONE(self->myerrno),
2382-
OR_NONE(self->strerror),
2383-
self->filename);
2392+
res = PyUnicode_FromFormat("[Errno %S] %S: %R",
2393+
OR_NONE(myerrno),
2394+
OR_NONE(strerror),
2395+
filename);
23842396
}
2397+
goto done;
23852398
}
2386-
if (self->myerrno && self->strerror)
2387-
return PyUnicode_FromFormat("[Errno %S] %S",
2388-
self->myerrno, self->strerror);
2389-
return BaseException_str(op);
2399+
if (myerrno && strerror) {
2400+
res = PyUnicode_FromFormat("[Errno %S] %S",
2401+
myerrno, strerror);
2402+
goto done;
2403+
}
2404+
res = BaseException_str(op);
2405+
done:
2406+
Py_XDECREF(myerrno);
2407+
Py_XDECREF(strerror);
2408+
Py_XDECREF(filename);
2409+
Py_XDECREF(filename2);
2410+
#ifdef MS_WINDOWS
2411+
Py_XDECREF(winerror);
2412+
#endif
2413+
return res;
23902414
}
23912415

23922416
static PyObject *

0 commit comments

Comments
 (0)