Skip to content

Conversation

@VanshAgarwal24036
Copy link
Contributor

Summary

When JSONDecodeError is user-replaced and re-enters during JSON parsing,
_raise_errmsg could reuse a freed exception type, leading to a
use-after-free.

This change holds a strong reference across the call and validates the
exception type before setting it, falling back safely when needed.

Issue

@VanshAgarwal24036
Copy link
Contributor Author

skip news

@ZeroIntensity
Copy link
Member

No, this affects user-facing APIs; please add a news entry.

Copy link
Member

@ZeroIntensity ZeroIntensity left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Please avoid making unnecessary code changes. They make it significantly more difficult to review.
  2. This is doing too much. The much simpler and more correct fix would be to simply move the Py_DECREF call on line 426 to after the if (exc) block.
  3. Please add a test case and news entry.

@bedevere-app
Copy link

bedevere-app bot commented Jan 8, 2026

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

@VanshAgarwal24036
Copy link
Contributor Author

Thanks for the review.

I’ve updated the patch to keep the fix minimal, added a regression test
that restores global state, and included a NEWS entry as requested.

Modules/_json.c Outdated
Comment on lines 418 to 420

PyObject *JSONDecodeError =
PyImport_ImportModuleAttr(&_Py_STR(json_decoder), &_Py_ID(JSONDecodeError));
PyImport_ImportModuleAttr(&_Py_STR(json_decoder), &_Py_ID(JSONDecodeError));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again please don't make unrelated formatting changes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert this change.

Modules/_json.c Outdated
Comment on lines 427 to 426
if (exc) {
PyObject *exc = PyObject_CallFunction(JSONDecodeError, "zOn", msg, s, end);
if (exc != NULL) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here. The only change we need is the movement of the Py_DECREF call.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert this change, only move Py_DECREF(JSONDecodeError);.

@VanshAgarwal24036
Copy link
Contributor Author

Thanks for the clarification. I’ve updated the test to only assert that the
interpreter doesn’t crash and pushed the changes.

@VanshAgarwal24036
Copy link
Contributor Author

The NEWS entry was removed per discussion above. Could someone please add the “skip news” label?

@VanshAgarwal24036
Copy link
Contributor Author

I have made the requested changes; please review again.

@bedevere-app
Copy link

bedevere-app bot commented Jan 9, 2026

Thanks for making the requested changes!

@ZeroIntensity: please review the changes made to this pull request.

Modules/_json.c Outdated
Comment on lines 418 to 420

PyObject *JSONDecodeError =
PyImport_ImportModuleAttr(&_Py_STR(json_decoder), &_Py_ID(JSONDecodeError));
PyImport_ImportModuleAttr(&_Py_STR(json_decoder), &_Py_ID(JSONDecodeError));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert this change.

Modules/_json.c Outdated
Comment on lines 427 to 426
if (exc) {
PyObject *exc = PyObject_CallFunction(JSONDecodeError, "zOn", msg, s, end);
if (exc != NULL) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert this change, only move Py_DECREF(JSONDecodeError);.


def test_reentrant_jsondecodeerror_does_not_crash(self):
# gh-143544
import json
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move this import at the module top level.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved import json to the module top level as requested. Thanks.

Modules/_json.c Outdated
Py_DECREF(exc);
}

/* Move DECREF after PyErr_SetObject */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that this comment is useful.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the comment as suggested.

Modules/_json.c Outdated
Comment on lines 418 to 421

PyObject *JSONDecodeError =
PyImport_ImportModuleAttr(&_Py_STR(json_decoder), &_Py_ID(JSONDecodeError));
PyImport_ImportModuleAttr(&_Py_STR(json_decoder),
&_Py_ID(JSONDecodeError));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have repeatedly asked you to not make unrelated changes. Sorry if this comes off as harsh, but if you refuse to pay attention to my review comments, I'm not going to spend time reviewing this.

Please review our AI policy. If you use an LLM for translation help, that's fine, but please try to consider what we're asking before copy-pasting the result of the LLM in your PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing this out. I’ve reverted all unrelated formatting changes and kept only the minimal fix (moving the Py_DECREF(JSONDecodeError) after PyErr_SetObject). Please review again.

{
/* Use JSONDecodeError exception to raise a nice looking ValueError subclass */
_Py_DECLARE_STR(json_decoder, "json.decoder");

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an unrelated change.

PyObject *exc;
exc = PyObject_CallFunction(JSONDecodeError, "zOn", msg, s, end);
Py_DECREF(JSONDecodeError);
PyObject *exc = PyObject_CallFunction(JSONDecodeError, "zOn", msg, s, end);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also an unrelated change.

Py_DECREF(JSONDecodeError);
}


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an unrelated change again.

Comment on lines +254 to +266
try:
json.JSONDecodeError = hook
json.decoder.JSONDecodeError = hook

# The exact exception type is not important here;
# this test only ensures we don't crash.
with self.assertRaises(Exception):
json.loads('"\\uZZZZ"')

finally:
json.JSONDecodeError = orig_json_error
json.decoder.JSONDecodeError = orig_decoder_error

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
try:
json.JSONDecodeError = hook
json.decoder.JSONDecodeError = hook
# The exact exception type is not important here;
# this test only ensures we don't crash.
with self.assertRaises(Exception):
json.loads('"\\uZZZZ"')
finally:
json.JSONDecodeError = orig_json_error
json.decoder.JSONDecodeError = orig_decoder_error
with (
support.swap_attr(json, "JSONDecodeError", hook),
support.swap_attr(json.decoder, "JSONDecodeError", hook)
):
# The exact exception type is not important here;
# this test only ensures we don't crash.
with self.assertRaises(Exception):
json.loads('"\\uZZZZ"')

I still think the exact type of exception is important. We want to be sure that we catch the correct regression and not an exception that was raised prior to the code being patched.

@@ -1,4 +1,5 @@
from test.test_json import PyTest, CTest
import json
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need this? I'm asking because TestFail is a mixin class with a self attribute for JSONDecoderError. So should we patch this module directly or not?

@bedevere-app
Copy link

bedevere-app bot commented Jan 9, 2026

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants