From b3f8754c8a0c743e9f80c06472ec7a7adc96f438 Mon Sep 17 00:00:00 2001 From: JustAnotherArchivist Date: Sun, 17 Apr 2022 04:04:40 +0000 Subject: [PATCH] Fix segmentation faults when handling unserialisable objects Errors during `__repr__` itself as well as ones during the conversion to a bytes object were not handled, resulting in NULL pointer dereferencing. Cf. #382 --- python/objToJSON.c | 11 +++++++++-- tests/test_ujson.py | 15 ++++++++++++--- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/python/objToJSON.c b/python/objToJSON.c index d7f6cb92..7d0076d9 100644 --- a/python/objToJSON.c +++ b/python/objToJSON.c @@ -670,8 +670,15 @@ static void Object_beginTypeContext (JSOBJ _obj, JSONTypeContext *tc, JSONObject PyErr_Clear(); objRepr = PyObject_Repr(obj); - PyObject* str = PyUnicode_AsEncodedString(objRepr, "utf-8", "~E~"); - PyErr_Format (PyExc_TypeError, "%s is not JSON serializable", PyBytes_AsString(str)); + if (!objRepr) + { + goto INVALID; + } + PyObject* str = PyUnicode_AsEncodedString(objRepr, "utf-8", "strict"); + if (str) + { + PyErr_Format (PyExc_TypeError, "%s is not JSON serializable", PyBytes_AsString(str)); + } Py_XDECREF(str); Py_DECREF(objRepr); diff --git a/tests/test_ujson.py b/tests/test_ujson.py index 3e3f3f1b..9c40b97b 100644 --- a/tests/test_ujson.py +++ b/tests/test_ujson.py @@ -636,8 +636,14 @@ def test_dumps(test_input, expected): class SomeObject: + def __init__(self, message, exception=None): + self._message = message + self._exception = exception + def __repr__(self): - return "Some Object" + if self._exception: + raise self._exception + return self._message @pytest.mark.parametrize( @@ -645,13 +651,16 @@ def __repr__(self): [ (set(), TypeError, "set() is not JSON serializable"), ({1, 2, 3}, TypeError, "{1, 2, 3} is not JSON serializable"), - (SomeObject(), TypeError, "Some Object is not JSON serializable"), + (SomeObject("Some Object"), TypeError, "Some Object is not JSON serializable"), + (SomeObject("\ud800"), UnicodeEncodeError, None), + (SomeObject(None, KeyboardInterrupt), KeyboardInterrupt, None), ], ) def test_dumps_raises(test_input, expected_exception, expected_message): with pytest.raises(expected_exception) as e: ujson.dumps(test_input) - assert str(e.value) == expected_message + if expected_message: + assert str(e.value) == expected_message @pytest.mark.parametrize(