From a043ba64bf100ccc11cb2fb18590d4da428bc9fb Mon Sep 17 00:00:00 2001 From: Oren Milman Date: Mon, 18 Sep 2017 12:08:29 +0300 Subject: [PATCH 1/5] init commit --- Lib/test/test_json/test_speedups.py | 21 +++++++++++++++++++ .../2017-09-18-12-07-39.bpo-31505.VomaFa.rst | 2 ++ Modules/_json.c | 19 +++++++++++++---- 3 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2017-09-18-12-07-39.bpo-31505.VomaFa.rst diff --git a/Lib/test/test_json/test_speedups.py b/Lib/test/test_json/test_speedups.py index 56f1882001d666..2805de104bca13 100644 --- a/Lib/test/test_json/test_speedups.py +++ b/Lib/test/test_json/test_speedups.py @@ -1,4 +1,5 @@ from test.test_json import CTest +import test.support class BadBool: @@ -36,6 +37,26 @@ def test_make_encoder(self): b"\xCD\x7D\x3D\x4E\x12\x4C\xF9\x79\xD7\x52\xBA\x82\xF2\x27\x4A\x7D\xA0\xCA\x75", None) + @test.support.cpython_only + def test_issueXXXXX(self): + # There shouldn't be an assertion failure in case c_make_encoder() + # receives a bad encoder() argument. + def _bad_encoder1(*args): + return None + enc = self.json.encoder.c_make_encoder(None, None, _bad_encoder1, None, + 'foo', 'bar', None, None, None) + with self.assertRaises(TypeError): + enc(obj='spam', _current_indent_level=4) + with self.assertRaises(TypeError): + enc(obj={'spam': 42}, _current_indent_level=4) + + def _bad_encoder2(*args): + 1/0 + enc = self.json.encoder.c_make_encoder(None, None, _bad_encoder2, None, + 'foo', 'bar', None, None, None) + with self.assertRaises(ZeroDivisionError): + enc(obj='spam', _current_indent_level=4) + def test_bad_bool_args(self): def test(name): self.json.encoder.JSONEncoder(**{name: BadBool()}).encode({'a': 1}) diff --git a/Misc/NEWS.d/next/Core and Builtins/2017-09-18-12-07-39.bpo-31505.VomaFa.rst b/Misc/NEWS.d/next/Core and Builtins/2017-09-18-12-07-39.bpo-31505.VomaFa.rst new file mode 100644 index 00000000000000..bad9e51aaf6108 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2017-09-18-12-07-39.bpo-31505.VomaFa.rst @@ -0,0 +1,2 @@ +Fix an assertion failure in `json`, in case `_json.make_encoder()` received +a bad `encoder()` argument. Patch by Oren Milman. diff --git a/Modules/_json.c b/Modules/_json.c index 769696d9d68558..53f521412454a2 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -1429,10 +1429,21 @@ static PyObject * encoder_encode_string(PyEncoderObject *s, PyObject *obj) { /* Return the JSON representation of a string */ - if (s->fast_encode) - return s->fast_encode(NULL, obj); - else - return PyObject_CallFunctionObjArgs(s->encoder, obj, NULL); + PyObject *encoded; + if (s->fast_encode) { + encoded = s->fast_encode(NULL, obj); + } + else { + encoded = PyObject_CallFunctionObjArgs(s->encoder, obj, NULL); + } + if (encoded != NULL && !PyUnicode_Check(encoded)) { + PyErr_Format(PyExc_TypeError, + "encoder() must return a string, not %.80s", + Py_TYPE(encoded)->tp_name); + Py_DECREF(encoded); + return NULL; + } + return encoded; } static int From fb91a877e6bb1c9cee037a9eca553de89ea3c89b Mon Sep 17 00:00:00 2001 From: Oren Milman Date: Mon, 18 Sep 2017 12:23:08 +0300 Subject: [PATCH 2/5] fix test name --- Lib/test/test_json/test_speedups.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_json/test_speedups.py b/Lib/test/test_json/test_speedups.py index 2805de104bca13..4c8ab20ae03563 100644 --- a/Lib/test/test_json/test_speedups.py +++ b/Lib/test/test_json/test_speedups.py @@ -38,7 +38,7 @@ def test_make_encoder(self): None) @test.support.cpython_only - def test_issueXXXXX(self): + def test_issue31505(self): # There shouldn't be an assertion failure in case c_make_encoder() # receives a bad encoder() argument. def _bad_encoder1(*args): From 99244b766e404406a27ea85eea35c16e5c49cec8 Mon Sep 17 00:00:00 2001 From: Oren Milman Date: Mon, 18 Sep 2017 12:38:57 +0300 Subject: [PATCH 3/5] improve patch C code --- Modules/_json.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Modules/_json.c b/Modules/_json.c index 53f521412454a2..89e0c94cfd8b9c 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -1430,12 +1430,10 @@ encoder_encode_string(PyEncoderObject *s, PyObject *obj) { /* Return the JSON representation of a string */ PyObject *encoded; - if (s->fast_encode) { - encoded = s->fast_encode(NULL, obj); - } - else { - encoded = PyObject_CallFunctionObjArgs(s->encoder, obj, NULL); - } + + if (s->fast_encode) + return s->fast_encode(NULL, obj); + encoded = PyObject_CallFunctionObjArgs(s->encoder, obj, NULL); if (encoded != NULL && !PyUnicode_Check(encoded)) { PyErr_Format(PyExc_TypeError, "encoder() must return a string, not %.80s", From da90d1eb7398447da229f8c1f15d34b9c02d35cb Mon Sep 17 00:00:00 2001 From: Oren Milman Date: Mon, 18 Sep 2017 19:53:22 +0300 Subject: [PATCH 4/5] various fixes according to Serhiy's advice --- Lib/test/test_json/test_speedups.py | 27 ++++++++++++++------------- Modules/_json.c | 3 ++- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/Lib/test/test_json/test_speedups.py b/Lib/test/test_json/test_speedups.py index 4c8ab20ae03563..88a81e027a33ea 100644 --- a/Lib/test/test_json/test_speedups.py +++ b/Lib/test/test_json/test_speedups.py @@ -37,25 +37,26 @@ def test_make_encoder(self): b"\xCD\x7D\x3D\x4E\x12\x4C\xF9\x79\xD7\x52\xBA\x82\xF2\x27\x4A\x7D\xA0\xCA\x75", None) - @test.support.cpython_only - def test_issue31505(self): - # There shouldn't be an assertion failure in case c_make_encoder() - # receives a bad encoder() argument. - def _bad_encoder1(*args): + def test_bad_str_encoder(self): + # Issue #31505: There shouldn't be an assertion failure in case + # c_make_encoder() receives a bad encoder() argument. + def bad_encoder1(*args): return None - enc = self.json.encoder.c_make_encoder(None, None, _bad_encoder1, None, - 'foo', 'bar', None, None, None) + enc = self.json.encoder.c_make_encoder(None, lambda obj: str(obj), + bad_encoder1, None, ': ', ', ', + False, False, False) with self.assertRaises(TypeError): - enc(obj='spam', _current_indent_level=4) + enc('spam', 4) with self.assertRaises(TypeError): - enc(obj={'spam': 42}, _current_indent_level=4) + enc({'spam': 42}, 4) - def _bad_encoder2(*args): + def bad_encoder2(*args): 1/0 - enc = self.json.encoder.c_make_encoder(None, None, _bad_encoder2, None, - 'foo', 'bar', None, None, None) + enc = self.json.encoder.c_make_encoder(None, lambda obj: str(obj), + bad_encoder2, None, ': ', ', ', + False, False, False) with self.assertRaises(ZeroDivisionError): - enc(obj='spam', _current_indent_level=4) + enc('spam', 4) def test_bad_bool_args(self): def test(name): diff --git a/Modules/_json.c b/Modules/_json.c index 89e0c94cfd8b9c..13218a6ecce587 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -1431,8 +1431,9 @@ encoder_encode_string(PyEncoderObject *s, PyObject *obj) /* Return the JSON representation of a string */ PyObject *encoded; - if (s->fast_encode) + if (s->fast_encode) { return s->fast_encode(NULL, obj); + } encoded = PyObject_CallFunctionObjArgs(s->encoder, obj, NULL); if (encoded != NULL && !PyUnicode_Check(encoded)) { PyErr_Format(PyExc_TypeError, From 014db749617530e0eea2c87d925513cc47ac8e99 Mon Sep 17 00:00:00 2001 From: Oren Milman Date: Tue, 19 Sep 2017 18:11:22 +0300 Subject: [PATCH 5/5] remove the redundant import --- Lib/test/test_json/test_speedups.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/test/test_json/test_speedups.py b/Lib/test/test_json/test_speedups.py index 88a81e027a33ea..5dad6920870421 100644 --- a/Lib/test/test_json/test_speedups.py +++ b/Lib/test/test_json/test_speedups.py @@ -1,5 +1,4 @@ from test.test_json import CTest -import test.support class BadBool: