gh-111942: Fix crashes in TextIOWrapper.reconfigure() (GH-111976)

* Fix crash when encoding is not string or None.
* Fix crash when both line_buffering and write_through raise exception
  when converted ti int.
* Add a number of tests for constructor and reconfigure() method
  with invalid arguments.
This commit is contained in:
Serhiy Storchaka 2023-11-14 17:37:56 +02:00 • committed by GitHub
parent a519b87958
commit ee06fffd38
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 122 additions and 5 deletions

View file

@ -1284,30 +1284,40 @@ textiowrapper_change_encoding(textio *self, PyObject *encoding,
errors = &_Py_ID(strict);
}
}
Py_INCREF(errors);
const char *c_encoding = PyUnicode_AsUTF8(encoding);
if (c_encoding == NULL) {
Py_DECREF(encoding);
Py_DECREF(errors);
return -1;
}
const char *c_errors = PyUnicode_AsUTF8(errors);
if (c_errors == NULL) {
Py_DECREF(encoding);
Py_DECREF(errors);
return -1;
}
// Create new encoder & decoder
PyObject *codec_info = _PyCodec_LookupTextEncoding(
PyUnicode_AsUTF8(encoding), "codecs.open()");
c_encoding, "codecs.open()");
if (codec_info == NULL) {
Py_DECREF(encoding);
Py_DECREF(errors);
return -1;
}
if (_textiowrapper_set_decoder(self, codec_info, c_errors) != 0 ||
_textiowrapper_set_encoder(self, codec_info, c_errors) != 0) {
Py_DECREF(codec_info);
Py_DECREF(encoding);
Py_DECREF(errors);
return -1;
}
Py_DECREF(codec_info);
Py_SETREF(self->encoding, encoding);
Py_SETREF(self->errors, Py_NewRef(errors));
Py_SETREF(self->errors, errors);
return _textiowrapper_fix_encoder_state(self);
}
@ -1338,6 +1348,26 @@ _io_TextIOWrapper_reconfigure_impl(textio *self, PyObject *encoding,
int write_through;
const char *newline = NULL;
if (encoding != Py_None && !PyUnicode_Check(encoding)) {
PyErr_Format(PyExc_TypeError,
"reconfigure() argument 'encoding' must be str or None, not %s",
Py_TYPE(encoding)->tp_name);
return NULL;
}
if (errors != Py_None && !PyUnicode_Check(errors)) {
PyErr_Format(PyExc_TypeError,
"reconfigure() argument 'errors' must be str or None, not %s",
Py_TYPE(errors)->tp_name);
return NULL;
}
if (newline_obj != NULL && newline_obj != Py_None &&
!PyUnicode_Check(newline_obj))
{
PyErr_Format(PyExc_TypeError,
"reconfigure() argument 'newline' must be str or None, not %s",
Py_TYPE(newline_obj)->tp_name);
return NULL;
}
/* Check if something is in the read buffer */
if (self->decoded_chars != NULL) {
if (encoding != Py_None || errors != Py_None || newline_obj != NULL) {
@ -1357,9 +1387,12 @@ _io_TextIOWrapper_reconfigure_impl(textio *self, PyObject *encoding,
line_buffering = convert_optional_bool(line_buffering_obj,
self->line_buffering);
if (line_buffering < 0) {
return NULL;
}
write_through = convert_optional_bool(write_through_obj,
self->write_through);
if (line_buffering < 0 || write_through < 0) {
if (write_through < 0) {
return NULL;
}