bpo-25455: Fixed crashes in repr of recursive buffered file-like obje… · pythoncapi/cpython@a5af6e1 · GitHub
Skip to content

Commit a5af6e1

Browse files
bpo-25455: Fixed crashes in repr of recursive buffered file-like objects. (python#514)
1 parent 77ed115 commit a5af6e1

6 files changed

Lines changed: 74 additions & 9 deletions

File tree

Lib/test/test_fileio.py

Lines changed: 7 additions & 1 deletion

Lib/test/test_io.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,6 +1014,16 @@ def test_repr(self):
10141014
raw.name = b"dummy"
10151015
self.assertEqual(repr(b), "<%s name=b'dummy'>" % clsname)
10161016

1017+
def test_recursive_repr(self):
1018+
# Issue #25455
1019+
raw = self.MockRawIO()
1020+
b = self.tp(raw)
1021+
with support.swap_attr(raw, 'name', b):
1022+
try:
1023+
repr(b) # Should not crash
1024+
except RuntimeError:
1025+
pass
1026+
10171027
def test_flush_error_on_close(self):
10181028
# Test that buffered file is closed despite failed flush
10191029
# and that flush() is called before file closed.
@@ -2435,6 +2445,16 @@ def test_repr(self):
24352445
t.buffer.detach()
24362446
repr(t) # Should not raise an exception
24372447

2448+
def test_recursive_repr(self):
2449+
# Issue #25455
2450+
raw = self.BytesIO()
2451+
t = self.TextIOWrapper(raw)
2452+
with support.swap_attr(raw, 'name', t):
2453+
try:
2454+
repr(t) # Should not crash
2455+
except RuntimeError:
2456+
pass
2457+
24382458
def test_line_buffering(self):
24392459
r = self.BytesIO()
24402460
b = self.BufferedWriter(r, 1000)

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,8 @@ Extension Modules
281281
Library
282282
-------
283283

284+
- bpo-25455: Fixed crashes in repr of recursive buffered file-like objects.
285+
284286
- bpo-29800: Fix crashes in partial.__repr__ if the keys of partial.keywords
285287
are not strings. Patch by Michael Seifert.
286288

Modules/_io/bufferedio.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1415,8 +1415,18 @@ buffered_repr(buffered *self)
14151415
res = PyUnicode_FromFormat("<%s>", Py_TYPE(self)->tp_name);
14161416
}
14171417
else {
1418-
res = PyUnicode_FromFormat("<%s name=%R>",
1419-
Py_TYPE(self)->tp_name, nameobj);
1418+
int status = Py_ReprEnter((PyObject *)self);
1419+
res = NULL;
1420+
if (status == 0) {
1421+
res = PyUnicode_FromFormat("<%s name=%R>",
1422+
Py_TYPE(self)->tp_name, nameobj);
1423+
Py_ReprLeave((PyObject *)self);
1424+
}
1425+
else if (status > 0) {
1426+
PyErr_Format(PyExc_RuntimeError,
1427+
"reentrant call inside %s.__repr__",
1428+
Py_TYPE(self)->tp_name);
1429+
}
14201430
Py_DECREF(nameobj);
14211431
}
14221432
return res;

Modules/_io/fileio.c

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,9 +1082,19 @@ fileio_repr(fileio *self)
10821082
self->fd, mode_string(self), self->closefd ? "True" : "False");
10831083
}
10841084
else {
1085-
res = PyUnicode_FromFormat(
1086-
"<_io.FileIO name=%R mode='%s' closefd=%s>",
1087-
nameobj, mode_string(self), self->closefd ? "True" : "False");
1085+
int status = Py_ReprEnter((PyObject *)self);
1086+
res = NULL;
1087+
if (status == 0) {
1088+
res = PyUnicode_FromFormat(
1089+
"<_io.FileIO name=%R mode='%s' closefd=%s>",
1090+
nameobj, mode_string(self), self->closefd ? "True" : "False");
1091+
Py_ReprLeave((PyObject *)self);
1092+
}
1093+
else if (status > 0) {
1094+
PyErr_Format(PyExc_RuntimeError,
1095+
"reentrant call inside %s.__repr__",
1096+
Py_TYPE(self)->tp_name);
1097+
}
10881098
Py_DECREF(nameobj);
10891099
}
10901100
return res;

Modules/_io/textio.c

Lines changed: 20 additions & 3 deletions

0 commit comments

Comments
 (0)