From 69a9fc760d393fca9219ca9caecd097d6b9c4833 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Mon, 6 Sep 2021 08:37:53 -0700 Subject: [PATCH] Fix refcount error in restore_sys_last_exception (#1816) --- docs/project/changelog.md | 7 ++++++- src/core/error_handling.c | 5 +++++ src/tests/test_pyodide.py | 26 ++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/docs/project/changelog.md b/docs/project/changelog.md index 0867bb66b..9b1eae4a6 100644 --- a/docs/project/changelog.md +++ b/docs/project/changelog.md @@ -34,10 +34,15 @@ substitutions: `PYODIDE_PACKAGES='*'` In addition, `make minimal` was removed, since it is now equivalent to `make` without extra arguments. {pr}`1801` -- {{Fix}} The `setInterruptBuffer` command is now publically exposed again, as +### Uncategorized + +- {{Fix}} The `setInterruptBuffer` command is now publicly exposed again, as it was before. {pr}`1797` +- {{Fix}} Fixed a use after free bug in the error handling code. + {pr}`1816` + ## Version 0.18.0 _August 3rd, 2021_ diff --git a/src/core/error_handling.c b/src/core/error_handling.c index 42a2c2f37..d96309ec4 100644 --- a/src/core/error_handling.c +++ b/src/core/error_handling.c @@ -118,6 +118,11 @@ restore_sys_last_exception(void* value) if (value != last_value) { return 0; } + // PyErr_Restore steals a reference to each of its arguments so need to incref + // them first. + Py_INCREF(last_type); + Py_INCREF(last_value); + Py_INCREF(last_traceback); PyErr_Restore(last_type, last_value, last_traceback); success = true; finally: diff --git a/src/tests/test_pyodide.py b/src/tests/test_pyodide.py index d2be68977..ca1af4c2b 100644 --- a/src/tests/test_pyodide.py +++ b/src/tests/test_pyodide.py @@ -573,6 +573,32 @@ def test_reentrant_error(selenium): assert caught +def test_restore_error(selenium): + # See PR #1816. + selenium.run_js( + """ + self.f = function(){ + pyodide.runPython(` + err = Exception('hi') + raise err + `); + } + pyodide.runPython(` + from js import f + import sys + try: + f() + except Exception as e: + assert err == e + assert e == sys.last_value + finally: + del err + assert sys.getrefcount(sys.last_value) == 2 + `); + """ + ) + + @pytest.mark.skip_refcount_check @pytest.mark.skip_pyproxy_check def test_custom_stdin_stdout(selenium_standalone_noload):