Issue #21310: Fixed possible resource leak in failed open().

This commit is contained in:
Serhiy Storchaka 2014-06-09 13:35:43 +03:00
commit 3a56117a60
4 changed files with 80 additions and 39 deletions

View File

@ -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:

View File

@ -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):

View File

@ -92,6 +92,8 @@ Core and Builtins
Library
-------
- Issue #21310: Fixed possible resource leak in failed open().
- Issue #21256: Printout of keyword args should be in deterministic order in
a mock function call. This will help to write better doctests.

View File

@ -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;
}