Commit 2fb00bb5 authored by Brett Cannon's avatar Brett Cannon

merge

parents 9de80ac1 d03ce4ae
...@@ -2542,6 +2542,30 @@ class TextIOWrapperTest(unittest.TestCase): ...@@ -2542,6 +2542,30 @@ class TextIOWrapperTest(unittest.TestCase):
txt.write('5') txt.write('5')
self.assertEqual(b''.join(raw._write_stack), b'123\n45') self.assertEqual(b''.join(raw._write_stack), b'123\n45')
def test_read_nonbytes(self):
# Issue #17106
# Crash when underlying read() returns non-bytes
t = self.TextIOWrapper(self.StringIO('a'))
self.assertRaises(TypeError, t.read, 1)
t = self.TextIOWrapper(self.StringIO('a'))
self.assertRaises(TypeError, t.readline)
t = self.TextIOWrapper(self.StringIO('a'))
self.assertRaises(TypeError, t.read)
def test_illegal_decoder(self):
# Issue #17106
# Crash when decoder returns non-string
t = self.TextIOWrapper(self.BytesIO(b'aaaaaa'), newline='\n',
encoding='quopri_codec')
self.assertRaises(TypeError, t.read, 1)
t = self.TextIOWrapper(self.BytesIO(b'aaaaaa'), newline='\n',
encoding='quopri_codec')
self.assertRaises(TypeError, t.readline)
t = self.TextIOWrapper(self.BytesIO(b'aaaaaa'), newline='\n',
encoding='quopri_codec')
self.assertRaises(TypeError, t.read)
class CTextIOWrapperTest(TextIOWrapperTest): class CTextIOWrapperTest(TextIOWrapperTest):
def test_initialization(self): def test_initialization(self):
......
...@@ -163,6 +163,10 @@ Core and Builtins ...@@ -163,6 +163,10 @@ Core and Builtins
Library Library
------- -------
- Issue #17106: Fix a segmentation fault in io.TextIOWrapper when an underlying
stream or a decoder produces data of an unexpected type (i.e. when
io.TextIOWrapper initialized with text stream or use bytes-to-bytes codec).
- Issue #15633: httplib.HTTPResponse is now mark closed when the server - Issue #15633: httplib.HTTPResponse is now mark closed when the server
sends less than the advertised Content-Length. sends less than the advertised Content-Length.
......
...@@ -257,6 +257,25 @@ incrementalnewlinedecoder_dealloc(nldecoder_object *self) ...@@ -257,6 +257,25 @@ incrementalnewlinedecoder_dealloc(nldecoder_object *self)
Py_TYPE(self)->tp_free((PyObject *)self); Py_TYPE(self)->tp_free((PyObject *)self);
} }
static int
check_decoded(PyObject *decoded)
{
if (decoded == NULL)
return -1;
if (!PyUnicode_Check(decoded)) {
PyErr_Format(PyExc_TypeError,
"decoder should return a string result, not '%.200s'",
Py_TYPE(decoded)->tp_name);
Py_DECREF(decoded);
return -1;
}
if (PyUnicode_READY(decoded) < 0) {
Py_DECREF(decoded);
return -1;
}
return 0;
}
#define SEEN_CR 1 #define SEEN_CR 1
#define SEEN_LF 2 #define SEEN_LF 2
#define SEEN_CRLF 4 #define SEEN_CRLF 4
...@@ -286,18 +305,9 @@ _PyIncrementalNewlineDecoder_decode(PyObject *_self, ...@@ -286,18 +305,9 @@ _PyIncrementalNewlineDecoder_decode(PyObject *_self,
Py_INCREF(output); Py_INCREF(output);
} }
if (output == NULL) if (check_decoded(output) < 0)
return NULL; return NULL;
if (!PyUnicode_Check(output)) {
PyErr_SetString(PyExc_TypeError,
"decoder should return a string result");
goto error;
}
if (PyUnicode_READY(output) == -1)
goto error;
output_len = PyUnicode_GET_LENGTH(output); output_len = PyUnicode_GET_LENGTH(output);
if (self->pendingcr && (final || output_len > 0)) { if (self->pendingcr && (final || output_len > 0)) {
/* Prefix output with CR */ /* Prefix output with CR */
...@@ -1458,7 +1468,13 @@ textiowrapper_read_chunk(textio *self, Py_ssize_t size_hint) ...@@ -1458,7 +1468,13 @@ textiowrapper_read_chunk(textio *self, Py_ssize_t size_hint)
Py_DECREF(chunk_size); Py_DECREF(chunk_size);
if (input_chunk == NULL) if (input_chunk == NULL)
goto fail; goto fail;
assert(PyBytes_Check(input_chunk)); if (!PyBytes_Check(input_chunk)) {
PyErr_Format(PyExc_TypeError,
"underlying %s() should have returned a bytes object, "
"not '%.200s'", (self->has_read1 ? "read1": "read"),
Py_TYPE(input_chunk)->tp_name);
goto fail;
}
nbytes = PyBytes_Size(input_chunk); nbytes = PyBytes_Size(input_chunk);
eof = (nbytes == 0); eof = (nbytes == 0);
...@@ -1472,10 +1488,7 @@ textiowrapper_read_chunk(textio *self, Py_ssize_t size_hint) ...@@ -1472,10 +1488,7 @@ textiowrapper_read_chunk(textio *self, Py_ssize_t size_hint)
_PyIO_str_decode, input_chunk, eof ? Py_True : Py_False, NULL); _PyIO_str_decode, input_chunk, eof ? Py_True : Py_False, NULL);
} }
/* TODO sanity check: isinstance(decoded_chars, unicode) */ if (check_decoded(decoded_chars) < 0)
if (decoded_chars == NULL)
goto fail;
if (PyUnicode_READY(decoded_chars) == -1)
goto fail; goto fail;
textiowrapper_set_decoded_chars(self, decoded_chars); textiowrapper_set_decoded_chars(self, decoded_chars);
nchars = PyUnicode_GET_LENGTH(decoded_chars); nchars = PyUnicode_GET_LENGTH(decoded_chars);
...@@ -1493,7 +1506,14 @@ textiowrapper_read_chunk(textio *self, Py_ssize_t size_hint) ...@@ -1493,7 +1506,14 @@ textiowrapper_read_chunk(textio *self, Py_ssize_t size_hint)
PyObject *next_input = PyNumber_Add(dec_buffer, input_chunk); PyObject *next_input = PyNumber_Add(dec_buffer, input_chunk);
if (next_input == NULL) if (next_input == NULL)
goto fail; goto fail;
assert (PyBytes_Check(next_input)); if (!PyBytes_Check(next_input)) {
PyErr_Format(PyExc_TypeError,
"decoder getstate() should have returned a bytes "
"object, not '%.200s'",
Py_TYPE(next_input)->tp_name);
Py_DECREF(next_input);
goto fail;
}
Py_DECREF(dec_buffer); Py_DECREF(dec_buffer);
Py_CLEAR(self->snapshot); Py_CLEAR(self->snapshot);
self->snapshot = Py_BuildValue("NN", dec_flags, next_input); self->snapshot = Py_BuildValue("NN", dec_flags, next_input);
...@@ -1542,7 +1562,7 @@ textiowrapper_read(textio *self, PyObject *args) ...@@ -1542,7 +1562,7 @@ textiowrapper_read(textio *self, PyObject *args)
decoded = PyObject_CallMethodObjArgs( decoded = PyObject_CallMethodObjArgs(
self->decoder, _PyIO_str_decode, bytes, Py_True, NULL); self->decoder, _PyIO_str_decode, bytes, Py_True, NULL);
Py_DECREF(bytes); Py_DECREF(bytes);
if (decoded == NULL) if (check_decoded(decoded) < 0)
goto fail; goto fail;
result = textiowrapper_get_decoded_chars(self, -1); result = textiowrapper_get_decoded_chars(self, -1);
...@@ -2151,7 +2171,14 @@ textiowrapper_seek(textio *self, PyObject *args) ...@@ -2151,7 +2171,14 @@ textiowrapper_seek(textio *self, PyObject *args)
if (input_chunk == NULL) if (input_chunk == NULL)
goto fail; goto fail;
assert (PyBytes_Check(input_chunk)); if (!PyBytes_Check(input_chunk)) {
PyErr_Format(PyExc_TypeError,
"underlying read() should have returned a bytes "
"object, not '%.200s'",
Py_TYPE(input_chunk)->tp_name);
Py_DECREF(input_chunk);
goto fail;
}
self->snapshot = Py_BuildValue("iN", cookie.dec_flags, input_chunk); self->snapshot = Py_BuildValue("iN", cookie.dec_flags, input_chunk);
if (self->snapshot == NULL) { if (self->snapshot == NULL) {
...@@ -2162,12 +2189,8 @@ textiowrapper_seek(textio *self, PyObject *args) ...@@ -2162,12 +2189,8 @@ textiowrapper_seek(textio *self, PyObject *args)
decoded = _PyObject_CallMethodId(self->decoder, &PyId_decode, decoded = _PyObject_CallMethodId(self->decoder, &PyId_decode,
"Oi", input_chunk, (int)cookie.need_eof); "Oi", input_chunk, (int)cookie.need_eof);
if (decoded == NULL) if (check_decoded(decoded) < 0)
goto fail; goto fail;
if (PyUnicode_READY(decoded) == -1) {
Py_DECREF(decoded);
goto fail;
}
textiowrapper_set_decoded_chars(self, decoded); textiowrapper_set_decoded_chars(self, decoded);
...@@ -2283,13 +2306,11 @@ textiowrapper_tell(textio *self, PyObject *args) ...@@ -2283,13 +2306,11 @@ textiowrapper_tell(textio *self, PyObject *args)
Py_DECREF(_state); \ Py_DECREF(_state); \
} while (0) } while (0)
/* TODO: replace assert with exception */
#define DECODER_DECODE(start, len, res) do { \ #define DECODER_DECODE(start, len, res) do { \
PyObject *_decoded = _PyObject_CallMethodId( \ PyObject *_decoded = _PyObject_CallMethodId( \
self->decoder, &PyId_decode, "y#", start, len); \ self->decoder, &PyId_decode, "y#", start, len); \
if (_decoded == NULL) \ if (check_decoded(_decoded) < 0) \
goto fail; \ goto fail; \
assert (PyUnicode_Check(_decoded)); \
res = PyUnicode_GET_LENGTH(_decoded); \ res = PyUnicode_GET_LENGTH(_decoded); \
Py_DECREF(_decoded); \ Py_DECREF(_decoded); \
} while (0) } while (0)
...@@ -2370,9 +2391,8 @@ textiowrapper_tell(textio *self, PyObject *args) ...@@ -2370,9 +2391,8 @@ textiowrapper_tell(textio *self, PyObject *args)
/* We didn't get enough decoded data; signal EOF to get more. */ /* We didn't get enough decoded data; signal EOF to get more. */
PyObject *decoded = _PyObject_CallMethodId( PyObject *decoded = _PyObject_CallMethodId(
self->decoder, &PyId_decode, "yi", "", /* final = */ 1); self->decoder, &PyId_decode, "yi", "", /* final = */ 1);
if (decoded == NULL) if (check_decoded(decoded) < 0)
goto fail; goto fail;
assert (PyUnicode_Check(decoded));
chars_decoded += PyUnicode_GET_LENGTH(decoded); chars_decoded += PyUnicode_GET_LENGTH(decoded);
Py_DECREF(decoded); Py_DECREF(decoded);
cookie.need_eof = 1; cookie.need_eof = 1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment