1use crate::follow::Follow;
2use crate::{ForwardsUOffset, SOffsetT, SkipSizePrefix, UOffsetT, VOffsetT, Vector, SIZE_UOFFSET};
3#[cfg(not(feature = "std"))]
4use alloc::vec::Vec;
5use core::ops::Range;
6use core::option::Option;
7
8#[cfg(not(feature = "std"))]
9use alloc::borrow::Cow;
10#[cfg(feature = "std")]
11use std::borrow::Cow;
12
13#[cfg(all(nightly, not(feature = "std")))]
14use core::error::Error;
15#[cfg(feature = "std")]
16use std::error::Error;
17
18#[derive(Clone, Debug, PartialEq, Eq)]
22pub enum ErrorTraceDetail {
23 VectorElement { index: usize, position: usize },
24 TableField { field_name: Cow<'static, str>, position: usize },
25 UnionVariant { variant: Cow<'static, str>, position: usize },
26}
27
28#[derive(PartialEq, Eq, Default, Debug, Clone)]
29pub struct ErrorTrace(Vec<ErrorTraceDetail>);
30
31impl core::convert::AsRef<[ErrorTraceDetail]> for ErrorTrace {
32 #[inline]
33 fn as_ref(&self) -> &[ErrorTraceDetail] {
34 &self.0
35 }
36}
37
38#[derive(Clone, Debug, PartialEq, Eq)]
41pub enum InvalidFlatbuffer {
42 MissingRequiredField {
43 required: Cow<'static, str>,
44 error_trace: ErrorTrace,
45 },
46 InconsistentUnion {
47 field: Cow<'static, str>,
48 field_type: Cow<'static, str>,
49 error_trace: ErrorTrace,
50 },
51 Utf8Error {
52 error: core::str::Utf8Error,
53 range: Range<usize>,
54 error_trace: ErrorTrace,
55 },
56 MissingNullTerminator {
57 range: Range<usize>,
58 error_trace: ErrorTrace,
59 },
60 Unaligned {
61 position: usize,
62 unaligned_type: Cow<'static, str>,
63 error_trace: ErrorTrace,
64 },
65 RangeOutOfBounds {
66 range: Range<usize>,
67 error_trace: ErrorTrace,
68 },
69 SignedOffsetOutOfBounds {
70 soffset: SOffsetT,
71 position: usize,
72 error_trace: ErrorTrace,
73 },
74 TooManyTables,
76 ApparentSizeTooLarge,
77 DepthLimitReached,
78}
79
80#[cfg(any(nightly, feature = "std"))]
81impl Error for InvalidFlatbuffer {
82 fn source(&self) -> Option<&(dyn Error + 'static)> {
83 if let InvalidFlatbuffer::Utf8Error { error: source, .. } = self {
84 Some(source)
85 } else {
86 None
87 }
88 }
89}
90
91impl core::fmt::Display for InvalidFlatbuffer {
92 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
93 match self {
94 InvalidFlatbuffer::MissingRequiredField { required, error_trace } => {
95 writeln!(f, "Missing required field `{}`.\n{}", required, error_trace)?;
96 }
97 InvalidFlatbuffer::InconsistentUnion { field, field_type, error_trace } => {
98 writeln!(
99 f,
100 "Exactly one of union discriminant (`{}`) and value (`{}`) are present.\n{}",
101 field_type, field, error_trace
102 )?;
103 }
104 InvalidFlatbuffer::Utf8Error { error, range, error_trace } => {
105 writeln!(f, "Utf8 error for string in {:?}: {}\n{}", range, error, error_trace)?;
106 }
107 InvalidFlatbuffer::MissingNullTerminator { range, error_trace } => {
108 writeln!(
109 f,
110 "String in range [{}, {}) is missing its null terminator.\n{}",
111 range.start, range.end, error_trace
112 )?;
113 }
114 InvalidFlatbuffer::Unaligned { position, unaligned_type, error_trace } => {
115 writeln!(
116 f,
117 "Type `{}` at position {} is unaligned.\n{}",
118 unaligned_type, position, error_trace
119 )?;
120 }
121 InvalidFlatbuffer::RangeOutOfBounds { range, error_trace } => {
122 writeln!(
123 f,
124 "Range [{}, {}) is out of bounds.\n{}",
125 range.start, range.end, error_trace
126 )?;
127 }
128 InvalidFlatbuffer::SignedOffsetOutOfBounds { soffset, position, error_trace } => {
129 writeln!(
130 f,
131 "Signed offset at position {} has value {} which points out of bounds.\n{}",
132 position, soffset, error_trace
133 )?;
134 }
135 InvalidFlatbuffer::TooManyTables {} => {
136 writeln!(f, "Too many tables.")?;
137 }
138 InvalidFlatbuffer::ApparentSizeTooLarge {} => {
139 writeln!(f, "Apparent size too large.")?;
140 }
141 InvalidFlatbuffer::DepthLimitReached {} => {
142 writeln!(f, "Nested table depth limit reached.")?;
143 }
144 }
145 Ok(())
146 }
147}
148
149impl core::fmt::Display for ErrorTrace {
150 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
151 use ErrorTraceDetail::*;
152 for e in self.0.iter() {
153 match e {
154 VectorElement { index, position } => {
155 writeln!(
156 f,
157 "\twhile verifying vector element {:?} at position {:?}",
158 index, position
159 )?;
160 }
161 TableField { field_name, position } => {
162 writeln!(
163 f,
164 "\twhile verifying table field `{}` at position {:?}",
165 field_name, position
166 )?;
167 }
168 UnionVariant { variant, position } => {
169 writeln!(
170 f,
171 "\t while verifying union variant `{}` at position {:?}",
172 variant, position
173 )?;
174 }
175 }
176 }
177 Ok(())
178 }
179}
180
181pub type Result<T> = core::result::Result<T, InvalidFlatbuffer>;
182
183impl InvalidFlatbuffer {
184 fn new_range_oob<T>(start: usize, end: usize) -> Result<T> {
185 Err(Self::RangeOutOfBounds { range: Range { start, end }, error_trace: Default::default() })
186 }
187 pub fn new_inconsistent_union<T>(
188 field: impl Into<Cow<'static, str>>,
189 field_type: impl Into<Cow<'static, str>>,
190 ) -> Result<T> {
191 Err(Self::InconsistentUnion {
192 field: field.into(),
193 field_type: field_type.into(),
194 error_trace: Default::default(),
195 })
196 }
197 pub fn new_missing_required<T>(required: impl Into<Cow<'static, str>>) -> Result<T> {
198 Err(Self::MissingRequiredField {
199 required: required.into(),
200 error_trace: Default::default(),
201 })
202 }
203}
204
205fn append_trace<T>(mut res: Result<T>, d: ErrorTraceDetail) -> Result<T> {
207 if let Err(e) = res.as_mut() {
208 use InvalidFlatbuffer::*;
209 if let MissingRequiredField { error_trace, .. }
210 | Unaligned { error_trace, .. }
211 | RangeOutOfBounds { error_trace, .. }
212 | InconsistentUnion { error_trace, .. }
213 | Utf8Error { error_trace, .. }
214 | MissingNullTerminator { error_trace, .. }
215 | SignedOffsetOutOfBounds { error_trace, .. } = e
216 {
217 error_trace.0.push(d)
218 }
219 }
220 res
221}
222
223fn trace_field<T>(res: Result<T>, field_name: Cow<'static, str>, position: usize) -> Result<T> {
225 append_trace(res, ErrorTraceDetail::TableField { field_name, position })
226}
227
228fn trace_elem<T>(res: Result<T>, index: usize, position: usize) -> Result<T> {
230 append_trace(res, ErrorTraceDetail::VectorElement { index, position })
231}
232
233#[derive(Debug, Clone, PartialEq, Eq)]
234pub struct VerifierOptions {
235 pub max_depth: usize,
237 pub max_tables: usize,
239 pub max_apparent_size: usize,
242 pub ignore_missing_null_terminator: bool,
245 }
249
250impl Default for VerifierOptions {
251 fn default() -> Self {
252 Self {
253 max_depth: 64,
254 max_tables: 1_000_000,
255 max_apparent_size: 1 << 31,
257 ignore_missing_null_terminator: false,
258 }
259 }
260}
261
262#[derive(Debug)]
264pub struct Verifier<'opts, 'buf> {
265 buffer: &'buf [u8],
266 opts: &'opts VerifierOptions,
267 depth: usize,
268 num_tables: usize,
269 apparent_size: usize,
270}
271
272impl<'opts, 'buf> Verifier<'opts, 'buf> {
273 pub fn new(opts: &'opts VerifierOptions, buffer: &'buf [u8]) -> Self {
274 Self { opts, buffer, depth: 0, num_tables: 0, apparent_size: 0 }
275 }
276 #[inline]
278 pub fn reset(&mut self) {
279 self.depth = 0;
280 self.num_tables = 0;
281 self.num_tables = 0;
282 }
283 #[inline]
294 pub fn is_aligned<T>(&self, pos: usize) -> Result<()> {
295 if pos % core::mem::align_of::<T>() == 0 {
296 Ok(())
297 } else {
298 Err(InvalidFlatbuffer::Unaligned {
299 unaligned_type: Cow::Borrowed(core::any::type_name::<T>()),
300 position: pos,
301 error_trace: Default::default(),
302 })
303 }
304 }
305 #[inline]
306 pub fn range_in_buffer(&mut self, pos: usize, size: usize) -> Result<()> {
307 let end = pos.saturating_add(size);
308 if end > self.buffer.len() {
309 return InvalidFlatbuffer::new_range_oob(pos, end);
310 }
311 self.apparent_size += size;
312 if self.apparent_size > self.opts.max_apparent_size {
313 return Err(InvalidFlatbuffer::ApparentSizeTooLarge);
314 }
315 Ok(())
316 }
317 #[inline]
319 pub fn in_buffer<T>(&mut self, pos: usize) -> Result<()> {
320 self.is_aligned::<T>(pos)?;
321 self.range_in_buffer(pos, core::mem::size_of::<T>())
322 }
323 #[inline]
324 pub fn get_u8(&mut self, pos: usize) -> Result<u8> {
325 self.in_buffer::<u8>(pos)?;
326 Ok(u8::from_le_bytes([self.buffer[pos]]))
327 }
328 #[inline]
329 fn get_u16(&mut self, pos: usize) -> Result<u16> {
330 self.in_buffer::<u16>(pos)?;
331 Ok(u16::from_le_bytes([self.buffer[pos], self.buffer[pos + 1]]))
332 }
333 #[inline]
334 pub fn get_uoffset(&mut self, pos: usize) -> Result<UOffsetT> {
335 self.in_buffer::<u32>(pos)?;
336 Ok(u32::from_le_bytes([
337 self.buffer[pos],
338 self.buffer[pos + 1],
339 self.buffer[pos + 2],
340 self.buffer[pos + 3],
341 ]))
342 }
343 #[inline]
344 fn deref_soffset(&mut self, pos: usize) -> Result<usize> {
345 self.in_buffer::<SOffsetT>(pos)?;
346 let offset = SOffsetT::from_le_bytes([
347 self.buffer[pos],
348 self.buffer[pos + 1],
349 self.buffer[pos + 2],
350 self.buffer[pos + 3],
351 ]);
352
353 let derefed = if offset > 0 {
355 pos.checked_sub(offset.unsigned_abs() as usize)
356 } else {
357 pos.checked_add(offset.unsigned_abs() as usize)
358 };
359 if let Some(x) = derefed {
360 if x < self.buffer.len() {
361 return Ok(x);
362 }
363 }
364 Err(InvalidFlatbuffer::SignedOffsetOutOfBounds {
365 soffset: offset,
366 position: pos,
367 error_trace: Default::default(),
368 })
369 }
370 #[inline]
371 pub fn visit_table<'ver>(
372 &'ver mut self,
373 table_pos: usize,
374 ) -> Result<TableVerifier<'ver, 'opts, 'buf>> {
375 let vtable_pos = self.deref_soffset(table_pos)?;
376 let vtable_len = self.get_u16(vtable_pos)? as usize;
377 self.is_aligned::<VOffsetT>(vtable_pos.saturating_add(vtable_len))?; self.range_in_buffer(vtable_pos, vtable_len)?;
379 self.num_tables += 1;
381 if self.num_tables > self.opts.max_tables {
382 return Err(InvalidFlatbuffer::TooManyTables);
383 }
384 self.depth += 1;
385 if self.depth > self.opts.max_depth {
386 return Err(InvalidFlatbuffer::DepthLimitReached);
387 }
388 Ok(TableVerifier { pos: table_pos, vtable: vtable_pos, vtable_len, verifier: self })
389 }
390
391 pub fn verify_union_variant<T: Verifiable>(
394 &mut self,
395 variant: impl Into<Cow<'static, str>>,
396 position: usize,
397 ) -> Result<()> {
398 let res = T::run_verifier(self, position);
399 append_trace(res, ErrorTraceDetail::UnionVariant { variant: variant.into(), position })
400 }
401}
402
403pub struct TableVerifier<'ver, 'opts, 'buf> {
406 pos: usize,
408 vtable: usize,
410 vtable_len: usize,
412 verifier: &'ver mut Verifier<'opts, 'buf>,
414}
415
416impl<'ver, 'opts, 'buf> TableVerifier<'ver, 'opts, 'buf> {
417 pub fn deref(&mut self, field: VOffsetT) -> Result<Option<usize>> {
418 let field = field as usize;
419 if field < self.vtable_len {
420 let field_offset = self.verifier.get_u16(self.vtable.saturating_add(field))?;
421 if field_offset > 0 {
422 let field_pos = self.pos.saturating_add(field_offset as usize);
424 return Ok(Some(field_pos));
425 }
426 }
427 Ok(None)
428 }
429
430 #[inline]
431 pub fn verifier(&mut self) -> &mut Verifier<'opts, 'buf> {
432 self.verifier
433 }
434
435 #[inline]
436 pub fn visit_field<T: Verifiable>(
437 mut self,
438 field_name: impl Into<Cow<'static, str>>,
439 field: VOffsetT,
440 required: bool,
441 ) -> Result<Self> {
442 if let Some(field_pos) = self.deref(field)? {
443 trace_field(T::run_verifier(self.verifier, field_pos), field_name.into(), field_pos)?;
444 return Ok(self);
445 }
446 if required {
447 InvalidFlatbuffer::new_missing_required(field_name.into())
448 } else {
449 Ok(self)
450 }
451 }
452 #[inline]
453 pub fn visit_union<Key, UnionVerifier>(
457 mut self,
458 key_field_name: impl Into<Cow<'static, str>>,
459 key_field_voff: VOffsetT,
460 val_field_name: impl Into<Cow<'static, str>>,
461 val_field_voff: VOffsetT,
462 required: bool,
463 verify_union: UnionVerifier,
464 ) -> Result<Self>
465 where
466 Key: Follow<'buf> + Verifiable,
467 UnionVerifier:
468 (core::ops::FnOnce(<Key as Follow<'buf>>::Inner, &mut Verifier, usize) -> Result<()>),
469 {
471 let val_pos = self.deref(val_field_voff)?;
473 let key_pos = self.deref(key_field_voff)?;
474 match (key_pos, val_pos) {
475 (None, None) => {
476 if required {
477 InvalidFlatbuffer::new_missing_required(val_field_name.into())
478 } else {
479 Ok(self)
480 }
481 }
482 (Some(k), Some(v)) => {
483 trace_field(Key::run_verifier(self.verifier, k), key_field_name.into(), k)?;
484 let discriminant = unsafe { Key::follow(self.verifier.buffer, k) };
487 trace_field(
488 verify_union(discriminant, self.verifier, v),
489 val_field_name.into(),
490 v,
491 )?;
492 Ok(self)
493 }
494 _ => InvalidFlatbuffer::new_inconsistent_union(
495 key_field_name.into(),
496 val_field_name.into(),
497 ),
498 }
499 }
500 pub fn finish(self) -> &'ver mut Verifier<'opts, 'buf> {
501 self.verifier.depth -= 1;
502 self.verifier
503 }
504}
505
506pub trait Verifiable {
509 fn run_verifier(v: &mut Verifier, pos: usize) -> Result<()>;
512}
513
514impl<T: Verifiable> Verifiable for ForwardsUOffset<T> {
516 #[inline]
517 fn run_verifier(v: &mut Verifier, pos: usize) -> Result<()> {
518 let offset = v.get_uoffset(pos)? as usize;
519 let next_pos = offset.saturating_add(pos);
520 T::run_verifier(v, next_pos)
521 }
522}
523
524fn verify_vector_range<T>(v: &mut Verifier, pos: usize) -> Result<core::ops::Range<usize>> {
526 let len = v.get_uoffset(pos)? as usize;
527 let start = pos.saturating_add(SIZE_UOFFSET);
528 v.is_aligned::<T>(start)?;
529 let size = len.saturating_mul(core::mem::size_of::<T>());
530 let end = start.saturating_add(size);
531 v.range_in_buffer(start, size)?;
532 Ok(core::ops::Range { start, end })
533}
534
535pub trait SimpleToVerifyInSlice {}
536
537impl SimpleToVerifyInSlice for bool {}
538
539impl SimpleToVerifyInSlice for i8 {}
540
541impl SimpleToVerifyInSlice for u8 {}
542
543impl SimpleToVerifyInSlice for i16 {}
544
545impl SimpleToVerifyInSlice for u16 {}
546
547impl SimpleToVerifyInSlice for i32 {}
548
549impl SimpleToVerifyInSlice for u32 {}
550
551impl SimpleToVerifyInSlice for f32 {}
552
553impl SimpleToVerifyInSlice for i64 {}
554
555impl SimpleToVerifyInSlice for u64 {}
556
557impl SimpleToVerifyInSlice for f64 {}
558
559impl<T: SimpleToVerifyInSlice> Verifiable for Vector<'_, T> {
560 fn run_verifier(v: &mut Verifier, pos: usize) -> Result<()> {
561 verify_vector_range::<T>(v, pos)?;
562 Ok(())
563 }
564}
565
566impl<T: Verifiable> Verifiable for SkipSizePrefix<T> {
567 #[inline]
568 fn run_verifier(v: &mut Verifier, pos: usize) -> Result<()> {
569 T::run_verifier(v, pos.saturating_add(crate::SIZE_SIZEPREFIX))
570 }
571}
572
573impl<T: Verifiable> Verifiable for Vector<'_, ForwardsUOffset<T>> {
574 #[inline]
575 fn run_verifier(v: &mut Verifier, pos: usize) -> Result<()> {
576 let range = verify_vector_range::<ForwardsUOffset<T>>(v, pos)?;
577 let size = core::mem::size_of::<ForwardsUOffset<T>>();
578 for (i, element_pos) in range.step_by(size).enumerate() {
579 trace_elem(<ForwardsUOffset<T>>::run_verifier(v, element_pos), i, element_pos)?;
580 }
581 Ok(())
582 }
583}
584
585impl<'a> Verifiable for &'a str {
586 #[inline]
587 fn run_verifier(v: &mut Verifier, pos: usize) -> Result<()> {
588 let range = verify_vector_range::<u8>(v, pos)?;
589 let has_null_terminator = v.buffer.get(range.end).map(|&b| b == 0).unwrap_or(false);
590 let s = core::str::from_utf8(&v.buffer[range.clone()]);
591 if let Err(error) = s {
592 return Err(InvalidFlatbuffer::Utf8Error {
593 error,
594 range,
595 error_trace: Default::default(),
596 });
597 }
598 if !v.opts.ignore_missing_null_terminator && !has_null_terminator {
599 return Err(InvalidFlatbuffer::MissingNullTerminator {
600 range,
601 error_trace: Default::default(),
602 });
603 }
604 Ok(())
605 }
606}
607
608macro_rules! impl_verifiable_for {
610 ($T: ty) => {
611 impl Verifiable for $T {
612 #[inline]
613 fn run_verifier<'opts, 'buf>(v: &mut Verifier<'opts, 'buf>, pos: usize) -> Result<()> {
614 v.in_buffer::<$T>(pos)
615 }
616 }
617 };
618}
619impl_verifiable_for!(bool);
620impl_verifiable_for!(u8);
621impl_verifiable_for!(i8);
622impl_verifiable_for!(u16);
623impl_verifiable_for!(i16);
624impl_verifiable_for!(u32);
625impl_verifiable_for!(i32);
626impl_verifiable_for!(f32);
627impl_verifiable_for!(u64);
628impl_verifiable_for!(i64);
629impl_verifiable_for!(f64);