Skip to content

Reject extra array elements for fixed-length types (text and Value) - #484

Merged
Licenser merged 2 commits into
simd-lite:mainfrom
yuxi-liu-wired:pr/extra-elements
Oct 4, 2026
Merged

Licenser merged 2 commits into
simd-lite:mainfrom
yuxi-liu-wired:pr/extra-elements

Conversation

@yuxi-liu-wired

@yuxi-liu-wired yuxi-liu-wired commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

fix: reject extra array elements for fixed-length types

A tuple, array, tuple struct or struct read from an array asks the SeqAccess for exactly as many elements as it has, then stops. The elements it left unread stayed on the tape and were read as the values after the array, so the data silently shifted:

from_slice::<((u8, u8), u8)>("[[1,2,3],4]")          Ok(((1, 2), 3))
from_slice::<(u8, (u8,), u8)>("[1,[2,99,98],3]")     Ok((1, (2,), 99))
from_slice::<Vec<(u8, u8)>>("[[1,2,[9,9]],[4,5]]")   Ok([(1, 2), (9, 9)])

In other shapes it failed with unrelated errors (ExpectedArray, ExpectedString). serde_json and sonic-rs reject all of these.

After the visitor returns, the remaining element count must be 0. Otherwise it is an invalid_length error, as in serde::de::value::SeqDeserializer::end and serde_json's visit_array. The same check covers objects, where a visitor stops reading members early.

The Value deserializers got the same check in #488.

Tests:

  • fixed_length_sequences_reject_extra_elements
  • tuple_variants_reject_extra_elements (text and every Value, as asked in review)

Both fail on main and pass with the change. The full test suite passes, and cargo fmt --check and clippy are clean.

Found with a differential fuzzer comparing serde_json, simd-json, sonic-rs and jiter.

@Licenser Licenser left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

just some testy stuff

Comment thread src/tests/serde.rs Outdated
@Licenser

Licenser commented Oct 4, 2026

Copy link
Copy Markdown
Member

will need a re-base due to conflicts, otherwise 🚀

claude added 2 commits October 4, 2026 18:51
A tuple, array, tuple struct or struct read from an array asks the
`SeqAccess` for exactly as many elements as it has and then stops. The
elements it left unread stayed on the tape and were read as the values
that follow the array, so data silently shifted:

    from_slice::<((u8, u8), u8)>("[[1,2,3],4]")          Ok(((1, 2), 3))
    from_slice::<(u8, (u8,), u8)>("[1,[2,99,98],3]")     Ok((1, (2,), 99))
    from_slice::<Vec<(u8, u8)>>("[[1,2,[9,9]],[4,5]]")   Ok([(1, 2), (9, 9)])

and in other shapes failed with unrelated errors (`ExpectedArray`,
`ExpectedString`). serde_json and sonic-rs reject all of these.
(`from_owned_value` ignores the extra elements without shifting the
following values; it is not changed here.)

After the visitor returns, the remaining element count must be 0;
otherwise it is an `invalid_length` error, as in
`serde::de::value::SeqDeserializer::end` and serde_json's
`visit_array`. The same check covers objects (a visitor that stops
reading members early).

Test: `fixed_length_sequences_reject_extra_elements` (fails before this
change).

Found by a three-way differential fuzzer (serde_json, simd-json,
sonic-rs).
@yuxi-liu-wired

Copy link
Copy Markdown
Contributor Author

Rebased on main. Since #488 already brought in the Value side of this (visit_array in the owned and borrowed Value deserializers, with value_fixed_length_sequences_reject_extra_elements), I dropped that commit. What's left is the text deserializer fix and its two tests, fixed_length_sequences_reject_extra_elements and tuple_variants_reject_extra_elements. Both fail on main and pass here. The full test suite, fmt and clippy are clean.

@Licenser
Licenser merged commit bd1ccfa into simd-lite:main Oct 4, 2026
29 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants