Opened 3 weeks ago
Closed 3 weeks ago
#37418 closed Bug (fixed)
forms.JSONField crashes with RecursionError on deeply nested input
| Reported by: | Philip Sørensen | Owned by: | Philip Sørensen |
|---|---|---|---|
| Component: | Forms | Version: | 6.1 |
| Severity: | Normal | Keywords: | |
| Cc: | Philip Sørensen | Triage Stage: | Ready for checkin |
| Has patch: | yes | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
forms.JSONField.to_python() and bound_data() only catch json.JSONDecodeError. When the submitted value is nested more deeply than the recursion limit, json.loads() raises RecursionError instead, so the form crashes (HTTP 500) rather than showing "Enter a valid JSON.". About 4 KB of input is enough, well within DATA_UPLOAD_MAX_MEMORY_SIZE:
>>> from django import forms
>>> forms.JSONField().clean("[" * 100_000 + "]" * 100_000)
RecursionError: maximum recursion depth exceeded while decoding a JSON array from a unicode string
This affects any form or ModelForm with a JSONField, including the admin. The contrib.postgres HStoreField form field has the same pattern.
Patch: also catch RecursionError in those three places, with regression tests.
Change History (6)
follow-up: 5 comment:1 by , 3 weeks ago
| Has patch: | unset |
|---|---|
| Owner: | set to |
| Status: | new → assigned |
| Triage Stage: | Unreviewed → Accepted |
comment:2 by , 3 weeks ago
| Patch needs improvement: | set |
|---|
comment:3 by , 3 weeks ago
| Has patch: | set |
|---|---|
| Owner: | changed from to |
comment:4 by , 3 weeks ago
| Patch needs improvement: | unset |
|---|---|
| Triage Stage: | Accepted → Ready for checkin |
comment:5 by , 3 weeks ago
Replying to Jacob Walls:
Accepted with a caveat that this won't work reliably for free-threading builds, as discussed on the PR.
More discussion on the PR attributed the failure to recursion limits configured on the CI runners, rather than free-threading per se, so this part of the commentary should be ignored.
Accepted with a caveat that this won't work reliably for free-threading builds, as discussed on the PR.
I don't think we should go further than add this to a list of caveats about free-threading. For instance, I doubt that Django should start rolling its own
max_depthchecks when this feature is already handled by other serialization libraries. (I think Django should instead be prioritizing the new feature request to add swappable JSON serializers. There should be a Trac ticket for that soon.)In any case, catching this here is harmless.