Skip to content

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

Description

@renz011tzar

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.

/// Resets verifier internal state.
#[inline]
pub fn reset(&mut self) {
self.depth = 0;
self.num_tables = 0;
self.num_tables = 0;
}

/// 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions