From: Serhiy Storchaka Date: Mon, 9 Jun 2014 10:32:34 +0000 (+0300) Subject: Issue #21310: Fixed possible resource leak in failed open(). X-Git-Tag: v3.4.2rc1~414 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f10063e3c3dcd5fe5b99858fc618c574b6df7bf2;p=python Issue #21310: Fixed possible resource leak in failed open(). --- diff --git a/Lib/_pyio.py b/Lib/_pyio.py index b04d23a0c2..d4cfb6e126 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -200,38 +200,45 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None, (appending and "a" or "") + (updating and "+" or ""), closefd, opener=opener) - line_buffering = False - if buffering == 1 or buffering < 0 and raw.isatty(): - buffering = -1 - line_buffering = True - if buffering < 0: - buffering = DEFAULT_BUFFER_SIZE - try: - bs = os.fstat(raw.fileno()).st_blksize - except (OSError, AttributeError): - pass + result = raw + try: + line_buffering = False + if buffering == 1 or buffering < 0 and raw.isatty(): + buffering = -1 + line_buffering = True + if buffering < 0: + buffering = DEFAULT_BUFFER_SIZE + try: + bs = os.fstat(raw.fileno()).st_blksize + except (OSError, AttributeError): + pass + else: + if bs > 1: + buffering = bs + if buffering < 0: + raise ValueError("invalid buffering size") + if buffering == 0: + if binary: + return result + raise ValueError("can't have unbuffered text I/O") + if updating: + buffer = BufferedRandom(raw, buffering) + elif creating or writing or appending: + buffer = BufferedWriter(raw, buffering) + elif reading: + buffer = BufferedReader(raw, buffering) else: - if bs > 1: - buffering = bs - if buffering < 0: - raise ValueError("invalid buffering size") - if buffering == 0: + raise ValueError("unknown mode: %r" % mode) + result = buffer if binary: - return raw - raise ValueError("can't have unbuffered text I/O") - if updating: - buffer = BufferedRandom(raw, buffering) - elif creating or writing or appending: - buffer = BufferedWriter(raw, buffering) - elif reading: - buffer = BufferedReader(raw, buffering) - else: - raise ValueError("unknown mode: %r" % mode) - if binary: - return buffer - text = TextIOWrapper(buffer, encoding, errors, newline, line_buffering) - text.mode = mode - return text + return result + text = TextIOWrapper(buffer, encoding, errors, newline, line_buffering) + result = text + text.mode = mode + return result + except: + result.close() + raise class DocDescriptor: diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index a94b85aae3..1cf97dd2e6 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -653,6 +653,20 @@ class IOTest(unittest.TestCase): fileio.close() f2.readline() + def test_nonbuffered_textio(self): + with warnings.catch_warnings(record=True) as recorded: + with self.assertRaises(ValueError): + self.open(support.TESTFN, 'w', buffering=0) + support.gc_collect() + self.assertEqual(recorded, []) + + def test_invalid_newline(self): + with warnings.catch_warnings(record=True) as recorded: + with self.assertRaises(ValueError): + self.open(support.TESTFN, 'w', newline='invalid') + support.gc_collect() + self.assertEqual(recorded, []) + class CIOTest(IOTest): diff --git a/Misc/NEWS b/Misc/NEWS index 6185590dac..29f5b61064 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -22,6 +22,8 @@ Core and Builtins Library ------- +- Issue #21310: Fixed possible resource leak in failed open(). + - Issue #21677: Fixed chaining nonnormalized exceptions in io close() methods. - Issue #11709: Fix the pydoc.help function to not fail when sys.stdin is not a diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c index be0464c4fa..1dbd563e2c 100644 --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -235,11 +235,12 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds) char rawmode[6], *m; int line_buffering, isatty; - PyObject *raw, *modeobj = NULL, *buffer = NULL, *wrapper = NULL; + PyObject *raw, *modeobj = NULL, *buffer, *wrapper, *result = NULL; _Py_IDENTIFIER(isatty); _Py_IDENTIFIER(fileno); _Py_IDENTIFIER(mode); + _Py_IDENTIFIER(close); if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|sizzziO:open", kwlist, &file, &mode, &buffering, @@ -354,6 +355,7 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds) "OsiO", file, rawmode, closefd, opener); if (raw == NULL) return NULL; + result = raw; modeobj = PyUnicode_FromString(mode); if (modeobj == NULL) @@ -412,7 +414,7 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds) } Py_DECREF(modeobj); - return raw; + return result; } /* wraps into a buffered file */ @@ -433,15 +435,16 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds) buffer = PyObject_CallFunction(Buffered_class, "Oi", raw, buffering); } - Py_CLEAR(raw); if (buffer == NULL) goto error; + result = buffer; + Py_DECREF(raw); /* if binary, returns the buffered file */ if (binary) { Py_DECREF(modeobj); - return buffer; + return result; } /* wraps into a TextIOWrapper */ @@ -450,20 +453,35 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds) buffer, encoding, errors, newline, line_buffering); - Py_CLEAR(buffer); if (wrapper == NULL) goto error; + result = wrapper; + Py_DECREF(buffer); if (_PyObject_SetAttrId(wrapper, &PyId_mode, modeobj) < 0) goto error; Py_DECREF(modeobj); - return wrapper; + return result; error: - Py_XDECREF(raw); + if (result != NULL) { + PyObject *exc, *val, *tb; + PyErr_Fetch(&exc, &val, &tb); + if (_PyObject_CallMethodId(result, &PyId_close, NULL) != NULL) + PyErr_Restore(exc, val, tb); + else { + PyObject *val2; + PyErr_NormalizeException(&exc, &val, &tb); + Py_XDECREF(exc); + Py_XDECREF(tb); + PyErr_Fetch(&exc, &val2, &tb); + PyErr_NormalizeException(&exc, &val2, &tb); + PyException_SetContext(val2, val); + PyErr_Restore(exc, val2, tb); + } + Py_DECREF(result); + } Py_XDECREF(modeobj); - Py_XDECREF(buffer); - Py_XDECREF(wrapper); return NULL; }