Skip to content

Add opt-in wiping of library-owned temporary buffers - #1365

Open
MattJackson wants to merge 6 commits into
serde-rs:masterfrom
MattJackson:zeroize-owned-buffers
Open

MattJackson wants to merge 6 commits into
serde-rs:masterfrom
MattJackson:zeroize-owned-buffers

Conversation

@MattJackson

@MattJackson MattJackson commented Nov 1, 2026 •

Copy link
Copy Markdown

serde_json can leave plaintext in allocations that applications cannot reach to wipe: parser scratch, custom error text, and old serialization buffers released during growth. This adds an opt-in zeroize feature for those library-owned temporary heap buffers. Related to #874 and #1093; it does not add wiping to Value itself.

The feature covers parser scratch across slice, reader, and stream input; 128-bit and arbitrary-precision numeric scanning; binary heap arithmetic buffers used by float_roundtrip, including multiplication/Karatsuba temporaries; raw-value reader buffers and UTF-8 errors; hidden number/raw-value adapters before visitor ownership transfer; custom error text and Debug temporaries; and serialization output growth and failure before output is returned. Internal raw-value formatting and string-to-box shrinking also keep the temporary allocation under a wiping owner.

Growth copies into a new allocation, wipes the old allocation using the zeroize crate, then releases it. Private guarded fields handle early returns and unwinding. Public method signatures and returned value types are preserved, and no Drop implementation is added to Deserializer, Value, Number, or RawValue. With the feature disabled, the buffer helpers use the original Vec/String operations.

The ownership boundary is documented in the crate docs and README. Input, caller-supplied readers/writers, external I/O errors, and arbitrary Serialize/Deserialize/Display implementations remain their owners' responsibility. Returned values and owned strings delivered to visitors retain normal ownership, including partially constructed output discarded by visitors. A live parser/error may retain its most recent text. This does not cover aborts, forgotten objects, OS/allocator copies, or copies left by moves, the compiler, stack storage, or registers.

The dependency is optional, uses only zeroize's alloc support, and works with std or alloc. Enabling it requires Rust 1.85 because zeroize 1.9 requires that compiler. The default feature set and its Rust 1.71 requirement are unchanged. Wiping and replacing allocations add work when the feature is enabled; performance has not been measured.

Tests add independent non-wiping controls and observe only registered initialized allocation prefixes at their matching live deallocation, never freed memory or inferred owners. Coverage includes growth/reuse/drop, numeric limb clones/replacement/Karatsuba arithmetic, malformed input, partial construction, visitor/formatter rejection, unwinding, error conversion, output ownership, and API/JSON compatibility. Existing upstream fixtures and the math interface are retained. The existing CI workflow gains permanent std/alloc feature combinations, optional-feature MSRV and no-std target checks, and Miri coverage for these tests.

Validation on 19cdf6c033827bd9b36feb61ef19ed301f6906f5: upstream PR CI passed nightly Linux/Windows tests with the selected std/alloc combinations, Rust 1.71/1.85/beta/stable checks, the aarch64 no-std target, documentation, fuzz compilation, minimal versions, and Miri on four 32/64-bit and little/big-endian targets with strict provenance. The combined zeroize feature suite has 42 unit tests. Normal fork push CI also passed, including default/all-feature Clippy. Local validation was limited to cargo fmt --check and git diff --check; compilation, tests, and dependency resolution were not run locally.

This branch has not been deployed

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant