json: read every object key as data when buffering a value - #483
Merged
Merged
Conversation
With serde_json's `raw_value` feature enabled anywhere in a build, `serde_json::Value`'s `Deserialize` impl reads an object whose first key is `$serde_json::private::RawValue` as the JSON text in the string under that key, and parses it with a new recursion limit. The enum helpers, the extension-key arm in generated code, `Any`, and the reflective WKT decoder buffered untrusted input through that impl. They now buffer through `BufferedValue` and `BufferedObject`, which build the same `Value` and read every key as data, so the depth of a decode is bounded by the deserializer that feeds it.
Removes `#[doc(hidden)]` from the module, so that a type outside buffa that keeps untrusted JSON as a `serde_json::Value` can buffer it the same way. `BufferedValue` and `BufferedObject` derive `Clone`, `Debug` and `PartialEq` and convert into what they hold. `value`, `opt_value` and `object` are functions for `#[serde(deserialize_with)]`. The guide states that `serde_json/arbitrary_precision` is unsupported (#482).
|
All contributors have signed the CLA ✍️ ✅ |
iainmcgin
marked this pull request as ready for review
September 26, 2026 18:52
iainmcgin
enabled auto-merge
September 26, 2026 18:52
rpb-ant
approved these changes
Sep 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When any crate in a build enables
serde_json'sraw_valuefeature,serde_json::Value'sDeserializeimpl reads an object whose first key is$serde_json::private::RawValueas the JSON text in the string under that key, and parses that text with a new recursion limit. buffa buffered untrusted JSON through that impl, so a sender could nest such strings past the 128-level limit, and could supply a value that differs from the one in the request text. A 2.1 MB body of 19 levels overflowed a 384 KiB thread stack. Builds without the feature are not affected.The affected shapes were repeated, map-valued and explicit-presence enum fields, the value of a
"[...]"key on a message with an extension range, andgoogle.protobuf.Any, in generated messages and inDynamicMessage. They now buffer throughBufferedValueandBufferedObjectinbuffa::json_helpers::buffered, whose visitors build the sameValueand read every key as data. The module is public, withvalue,opt_valueandobjectfor#[serde(deserialize_with)], so that any type that keeps untrusted JSON as aserde_json::Valuecan use it.For review:
"[...]"arm is in generated code. Code generated before this change keeps the old arm until it is regenerated, and code generated after it needs this runtime. The BSR plugin must be republished with the release that carries this.serde_json/raw_valueis a dev-dependency feature ofbuffa,buffa-types,buffa-descriptorandbuffa-test, so the workspace tests run only with the feature on. One test per crate fails if it is off. The conformance crate still builds without it.serde_json/arbitrary_precision, a number with a fraction or an exponent, or outside thei64andu64ranges, decoded inside anAnypayload and an extension value, and is rejected after this change, as it already was in adoublefield. Where theAnypayload is agoogle.protobuf.Value, the number is read as a struct. Reading the privateNumberkey would let an object decode as a number in a build without the feature. json: a build withserde_json/arbitrary_precisionrejects every number outside thei64andu64integer ranges #482 tracks support.maincarries breaking changes, so a 0.9.x release needs this cherry-picked to a maintenance branch.