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.
Summary
Verifier::reset()assignsself.num_tables = 0twice and never resetsself.apparent_size. AVerifierreused viareset()therefore keeps accumulatingapparent_sizeacross uses, and once the running total exceedsmax_apparent_sizeit rejects every subsequent buffer withApparentSizeTooLarge, regardless of whether that buffer is valid.flatbuffers/rust/flatbuffers/src/verifier.rs
Lines 276 to 282 in 81edeb1
apparent_sizeis initialized innew()and incremented inrange_in_buffer(), but is never cleared anywhere:Reproduction
Executed against the crate at
master(81edeb17), version 25.12.19: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
Verifieracross messages (which is whatreset()exists for) will begin failing all verification once cumulative size passesmax_apparent_size(default1 << 31).Suggested fix
Happy to send a PR if useful.