diff options
author | Serhiy Storchaka <storchaka@gmail.com> | 2014-06-09 10:32:08 (GMT) |
---|---|---|
committer | Serhiy Storchaka <storchaka@gmail.com> | 2014-06-09 10:32:08 (GMT) |
commit | 05b0a1be37d02a8d47bbd997763ada85054d69d7 (patch) | |
tree | b0aff6beb420ecbdcca8269561540ce7bfea595b | |
parent | 6453a01a0c55a2687836cc3720ba2e8b0a18e985 (diff) | |
download | cpython-05b0a1be37d02a8d47bbd997763ada85054d69d7.zip cpython-05b0a1be37d02a8d47bbd997763ada85054d69d7.tar.gz cpython-05b0a1be37d02a8d47bbd997763ada85054d69d7.tar.bz2 |
Issue #21310: Fixed possible resource leak in failed open().
-rw-r--r-- | Lib/_pyio.py | 67 | ||||
-rw-r--r-- | Lib/test/test_io.py | 15 | ||||
-rw-r--r-- | Misc/NEWS | 2 | ||||
-rw-r--r-- | Modules/_io/_iomodule.c | 30 |
4 files changed, 75 insertions, 39 deletions
diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 1495994..3acbc65 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -192,38 +192,45 @@ def open(file, mode="r", buffering=-1, (appending and "a" or "") + (updating and "+" or ""), closefd) - 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 (os.error, 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 (os.error, 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 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 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 936dbed..af3b90a 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -29,6 +29,7 @@ import array import random import unittest import weakref +import warnings import abc import signal import errno @@ -603,6 +604,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): @@ -25,6 +25,8 @@ Core and Builtins Library ------- +- Issue #21310: Fixed possible resource leak in failed open(). + - Issue #21304: Backport the key derivation function hashlib.pbkdf2_hmac from Python 3 per PEP 466. diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c index a024d86..40acdf5 100644 --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -303,7 +303,7 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds) int line_buffering; long isatty; - PyObject *raw, *modeobj = NULL, *buffer = NULL, *wrapper = NULL; + PyObject *raw, *modeobj = NULL, *buffer, *wrapper, *result = NULL; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|sizzzi:open", kwlist, &file, &mode, &buffering, @@ -416,6 +416,7 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds) "Osi", file, rawmode, closefd); if (raw == NULL) return NULL; + result = raw; modeobj = PyUnicode_FromString(mode); if (modeobj == NULL) @@ -474,7 +475,7 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds) } Py_DECREF(modeobj); - return raw; + return result; } /* wraps into a buffered file */ @@ -495,15 +496,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 */ @@ -512,20 +514,30 @@ 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_SetAttrString(wrapper, "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_CallMethod(result, "close", NULL) != NULL) + PyErr_Restore(exc, val, tb); + else { + Py_XDECREF(exc); + Py_XDECREF(val); + Py_XDECREF(tb); + } + Py_DECREF(result); + } Py_XDECREF(modeobj); - Py_XDECREF(buffer); - Py_XDECREF(wrapper); return NULL; } |