]> granicus.if.org Git - python/commitdiff
bpo-25455: Fixed crashes in repr of recursive buffered file-like objects. (#514)...
authorSerhiy Storchaka <storchaka@gmail.com>
Sun, 19 Mar 2017 22:51:16 +0000 (00:51 +0200)
committerGitHub <noreply@github.com>
Sun, 19 Mar 2017 22:51:16 +0000 (00:51 +0200)
(cherry picked from commit a5af6e1af77ee0f9294c5776478a9c24d9fbab94)

Lib/test/test_fileio.py
Lib/test/test_io.py
Misc/NEWS
Modules/_io/bufferedio.c
Modules/_io/fileio.c
Modules/_io/textio.c

index 12f2f119b5e903ddec1d993830cddb5048988c38..1bc1847a360cc74fa1f51fabd6c6aed3bdbbd4cb 100644 (file)
@@ -9,7 +9,8 @@ from array import array
 from weakref import proxy
 from functools import wraps
 
-from test.support import TESTFN, check_warnings, run_unittest, make_bad_fd, cpython_only
+from test.support import (TESTFN, check_warnings, run_unittest,
+                          make_bad_fd, cpython_only, swap_attr)
 from collections import UserList
 
 import _io  # C implementation of io
@@ -175,6 +176,12 @@ class AutoFileTests:
         finally:
             os.close(fd)
 
+    def testRecursiveRepr(self):
+        # Issue #25455
+        with swap_attr(self.f, 'name', self.f):
+            with self.assertRaises(RuntimeError):
+                repr(self.f)  # Should not crash
+
     def testErrors(self):
         f = self.f
         self.assertFalse(f.isatty())
index 5111882a0258e90a408104344dff5467d4fac7ac..ab24ca110dd9294fafbb909c1467601d42ddbeef 100644 (file)
@@ -981,6 +981,16 @@ class CommonBufferedTests:
         raw.name = b"dummy"
         self.assertEqual(repr(b), "<%s name=b'dummy'>" % clsname)
 
+    def test_recursive_repr(self):
+        # Issue #25455
+        raw = self.MockRawIO()
+        b = self.tp(raw)
+        with support.swap_attr(raw, 'name', b):
+            try:
+                repr(b)  # Should not crash
+            except RuntimeError:
+                pass
+
     def test_flush_error_on_close(self):
         # Test that buffered file is closed despite failed flush
         # and that flush() is called before file closed.
@@ -2391,6 +2401,16 @@ class TextIOWrapperTest(unittest.TestCase):
         t.buffer.detach()
         repr(t)  # Should not raise an exception
 
+    def test_recursive_repr(self):
+        # Issue #25455
+        raw = self.BytesIO()
+        t = self.TextIOWrapper(raw)
+        with support.swap_attr(raw, 'name', t):
+            try:
+                repr(t)  # Should not crash
+            except RuntimeError:
+                pass
+
     def test_line_buffering(self):
         r = self.BytesIO()
         b = self.BufferedWriter(r, 1000)
index 40d87e291d0f6e491c162634b0254f2e0645dbcc..1005bc543588198eb832524984bf47851619e7ea 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -43,6 +43,8 @@ Extension Modules
 Library
 -------
 
+- bpo-25455: Fixed crashes in repr of recursive buffered file-like objects.
+
 - bpo-29800: Fix crashes in partial.__repr__ if the keys of partial.keywords
   are not strings.  Patch by Michael Seifert.
 
index 6d67751c7d6b08b85e7309ae86dde786190954dd..909c4fa84659e5f589f13bbc3c90b9aa1f079a7f 100644 (file)
@@ -1416,8 +1416,18 @@ buffered_repr(buffered *self)
         res = PyUnicode_FromFormat("<%s>", Py_TYPE(self)->tp_name);
     }
     else {
-        res = PyUnicode_FromFormat("<%s name=%R>",
-                                   Py_TYPE(self)->tp_name, nameobj);
+        int status = Py_ReprEnter((PyObject *)self);
+        res = NULL;
+        if (status == 0) {
+            res = PyUnicode_FromFormat("<%s name=%R>",
+                                       Py_TYPE(self)->tp_name, nameobj);
+            Py_ReprLeave((PyObject *)self);
+        }
+        else if (status > 0) {
+            PyErr_Format(PyExc_RuntimeError,
+                         "reentrant call inside %s.__repr__",
+                         Py_TYPE(self)->tp_name);
+        }
         Py_DECREF(nameobj);
     }
     return res;
index 919cf502dca4ac1df8ff8b0b15ffab4483dfeabc..c77f05521d080cc3e4f529075f88fba77b94a883 100644 (file)
@@ -1093,9 +1093,19 @@ fileio_repr(fileio *self)
             self->fd, mode_string(self), self->closefd ? "True" : "False");
     }
     else {
-        res = PyUnicode_FromFormat(
-            "<_io.FileIO name=%R mode='%s' closefd=%s>",
-            nameobj, mode_string(self), self->closefd ? "True" : "False");
+        int status = Py_ReprEnter((PyObject *)self);
+        res = NULL;
+        if (status == 0) {
+            res = PyUnicode_FromFormat(
+                "<_io.FileIO name=%R mode='%s' closefd=%s>",
+                nameobj, mode_string(self), self->closefd ? "True" : "False");
+            Py_ReprLeave((PyObject *)self);
+        }
+        else if (status > 0) {
+            PyErr_Format(PyExc_RuntimeError,
+                         "reentrant call inside %s.__repr__",
+                         Py_TYPE(self)->tp_name);
+        }
         Py_DECREF(nameobj);
     }
     return res;
index 89b0798cd0b6876fea2267c0d75511f1481a939a..3ab11387bf722a74297196794b4e3f0b95edec0a 100644 (file)
@@ -2483,6 +2483,7 @@ static PyObject *
 textiowrapper_repr(textio *self)
 {
     PyObject *nameobj, *modeobj, *res, *s;
+    int status;
 
     CHECK_INITIALIZED(self);
 
@@ -2490,6 +2491,15 @@ textiowrapper_repr(textio *self)
     if (res == NULL)
         return NULL;
 
+    status = Py_ReprEnter((PyObject *)self);
+    if (status != 0) {
+        if (status > 0) {
+            PyErr_Format(PyExc_RuntimeError,
+                         "reentrant call inside %s.__repr__",
+                         Py_TYPE(self)->tp_name);
+        }
+        goto error;
+    }
     nameobj = _PyObject_GetAttrId((PyObject *) self, &PyId_name);
     if (nameobj == NULL) {
         if (PyErr_ExceptionMatches(PyExc_Exception))
@@ -2504,7 +2514,7 @@ textiowrapper_repr(textio *self)
             goto error;
         PyUnicode_AppendAndDel(&res, s);
         if (res == NULL)
-            return NULL;
+            goto error;
     }
     modeobj = _PyObject_GetAttrId((PyObject *) self, &PyId_mode);
     if (modeobj == NULL) {
@@ -2520,14 +2530,21 @@ textiowrapper_repr(textio *self)
             goto error;
         PyUnicode_AppendAndDel(&res, s);
         if (res == NULL)
-            return NULL;
+            goto error;
     }
     s = PyUnicode_FromFormat("%U encoding=%R>",
                              res, self->encoding);
     Py_DECREF(res);
+    if (status == 0) {
+        Py_ReprLeave((PyObject *)self);
+    }
     return s;
-error:
+
+  error:
     Py_XDECREF(res);
+    if (status == 0) {
+        Py_ReprLeave((PyObject *)self);
+    }
     return NULL;
 }