Skip to content

Commit 2ccf620

Browse files
committed
bpo-34068: _io__IOBase_close_impl could call _PyObject_SetAttrId with an exception set
1 parent 16dfca4 commit 2ccf620

File tree

3 files changed

+19
-5
lines changed

3 files changed

+19
-5
lines changed

Lib/test/test_io.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -968,6 +968,16 @@ def read1(self, size):
968968
self.assertSequenceEqual(buffer[result:], unused)
969969
self.assertEqual(len(reader.avail), avail - result)
970970

971+
def test_close_assert(self):
972+
class R(self.IOBase):
973+
def __setattr__(self, name, value):
974+
pass
975+
def flush(self):
976+
raise OSError()
977+
f = R()
978+
# This would cause an assertion failure.
979+
self.assertRaises(OSError, f.close)
980+
971981

972982
class CIOTest(IOTest):
973983

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
In :meth:`io.IOBase.close`, ensure that ``_PyObject_SetAttrId()`` is not
2+
called with a live exception. Patch by Zackery Spytz and Serhiy Storchaka.

Modules/_io/iobase.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,8 @@ static PyObject *
224224
_io__IOBase_close_impl(PyObject *self)
225225
/*[clinic end generated code: output=63c6a6f57d783d6d input=f4494d5c31dbc6b7]*/
226226
{
227-
PyObject *res;
228-
int closed = iobase_is_closed(self);
227+
PyObject *res, *exc, *val, *tb;
228+
int rc, closed = iobase_is_closed(self);
229229

230230
if (closed < 0) {
231231
return NULL;
@@ -236,9 +236,11 @@ _io__IOBase_close_impl(PyObject *self)
236236

237237
res = PyObject_CallMethodObjArgs(self, _PyIO_str_flush, NULL);
238238

239-
if (_PyObject_SetAttrId(self, &PyId___IOBase_closed, Py_True) < 0) {
240-
Py_XDECREF(res);
241-
return NULL;
239+
PyErr_Fetch(&exc, &val, &tb);
240+
rc = _PyObject_SetAttrId(self, &PyId___IOBase_closed, Py_True);
241+
_PyErr_ChainExceptions(exc, val, tb);
242+
if (rc < 0) {
243+
Py_CLEAR(res);
242244
}
243245

244246
if (res == NULL)

0 commit comments

Comments
 (0)