Rust: Verifier::reset() does not reset apparent_size (duplicated num_tables assignment)

Open Beginner friendly
#9,189 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
1/5
Estimated time
Under an hour
Newbie friendliness
90/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Quiet
Tech stack
rust
Domain
backend

Research direction

Start in rust/flatbuffers/src/verifier.rs at Verifier::reset() and the apparent_size accounting in range_in_buffer(). Run the reproduction in rust/flatbuffers/tests/reset_apparent_size.rs; done means reset allows the identical range to pass again without triggering ApparentSizeTooLarge.

Written by the indexing model from the issue text.

Description

Summary

Verifier::reset() assigns self.num_tables = 0 twice and never resets self.apparent_size. A Verifier reused via reset() therefore keeps accumulating apparent_size across uses, and once the running total exceeds max_apparent_size it rejects every subsequent buffer with ApparentSizeTooLarge, regardless of whether that buffer is valid.

https://github.com/google/flatbuffers/blob/81edeb17d9118143f2c81caf27edfb0df401279e/rust/flatbuffers/src/verifier.rs#L276-L282

/// Resets verifier internal state.
#[inline]
pub fn reset(&mut self) {
    self.depth = 0;
    self.num_tables = 0;
    self.num_tables = 0;   // <-- duplicated; `self.apparent_size = 0;` appears to be intended
}

apparent_size is initialized in new() and incremented in range_in_buffer(), but is never cleared anywhere:

274:        Self { opts, buffer, depth: 0, num_tables: 0, apparent_size: 0 }
311:        self.apparent_size += size;
312:        if self.apparent_size > self.opts.max_apparent_size {
Reproduction

Executed against the crate at master (81edeb17), version 25.12.19:

// rust/flatbuffers/tests/reset_apparent_size.rs
use flatbuffers::{InvalidFlatbuffer, Verifier, VerifierOptions};

#[test]
fn reset_should_clear_apparent_size() {
    let opts = VerifierOptions { max_apparent_size: 100, ..Default::default() };
    let buf = [0u8; 64];
    let mut v = Verifier::new(&opts, &buf);

    v.range_in_buffer(0, 60).expect("first range must fit");
    v.reset();

    match v.range_in_buffer(0, 60) {
        Ok(()) => println!("reset() cleared apparent_size"),
        Err(InvalidFlatbuffer::ApparentSizeTooLarge) => {
            panic!("after reset(), apparent_size still held 60")
        }
        Err(e) => panic!("unexpected error: {e:?}"),
    }
}
$ cargo test --test reset_apparent_size
thread 'reset_should_clear_apparent_size' panicked at:
after reset(), apparent_size still held 60
test result: FAILED. 0 passed; 1 failed

The second call is identical to the first and should succeed after a documented state reset; instead the running total reaches 120 against the 100-byte limit and is rejected.

Impact

Correctness / availability, not a verification bypass — the effect is over-rejection of valid buffers, so it cannot cause malicious buffers to be accepted. Applications that reuse one Verifier across messages (which is what reset() exists for) will begin failing all verification once cumulative size passes max_apparent_size (default 1 << 31).

Suggested fix
pub fn reset(&mut self) {
    self.depth = 0;
    self.num_tables = 0;
    self.apparent_size = 0;
}

Happy to send a PR if useful.

Dominant language
C++
Stars
26.5k
Forks
3.7k
PR merge metrics
No merged PRs in 30d

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from google/flatbuffers

All issues in google/flatbuffers

Similar issues

More C++ issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.