xref: /aosp_15_r20/external/flatbuffers/rust/flatbuffers/src/verifier.rs (revision 890232f25432b36107d06881e0a25aaa6b473652)
1 #[cfg(feature = "no_std")]
2 use alloc::vec::Vec;
3 use core::ops::Range;
4 use core::option::Option;
5 use crate::follow::Follow;
6 use crate::{ForwardsUOffset, SOffsetT, SkipSizePrefix, UOffsetT, VOffsetT, Vector, SIZE_UOFFSET};
7 
8 #[cfg(feature="no_std")]
9 use thiserror_core2::Error;
10 #[cfg(not(feature="no_std"))]
11 use thiserror::Error;
12 
13 /// Traces the location of data errors. Not populated for Dos detecting errors.
14 /// Useful for MissingRequiredField and Utf8Error in particular, though
15 /// the other errors should not be producible by correct flatbuffers implementations.
16 #[derive(Clone, Debug, PartialEq, Eq)]
17 pub enum ErrorTraceDetail {
18     VectorElement {
19         index: usize,
20         position: usize,
21     },
22     TableField {
23         field_name: &'static str,
24         position: usize,
25     },
26     UnionVariant {
27         variant: &'static str,
28         position: usize,
29     },
30 }
31 #[derive(PartialEq, Eq, Default, Debug, Clone)]
32 pub struct ErrorTrace(Vec<ErrorTraceDetail>);
33 impl core::convert::AsRef<[ErrorTraceDetail]> for ErrorTrace {
34     #[inline]
as_ref(&self) -> &[ErrorTraceDetail]35     fn as_ref(&self) -> &[ErrorTraceDetail] {
36         &self.0
37     }
38 }
39 
40 /// Describes how a flatuffer is invalid and, for data errors, roughly where. No extra tracing
41 /// information is given for DoS detecting errors since it will probably be a lot.
42 #[derive(Clone, Error, Debug, PartialEq, Eq)]
43 pub enum InvalidFlatbuffer {
44     #[error("Missing required field `{required}`.\n{error_trace}")]
45     MissingRequiredField {
46         required: &'static str,
47         error_trace: ErrorTrace,
48     },
49     #[error(
50         "Union exactly one of union discriminant (`{field_type}`) and value \
51              (`{field}`) are present.\n{error_trace}"
52     )]
53     InconsistentUnion {
54         field: &'static str,
55         field_type: &'static str,
56         error_trace: ErrorTrace,
57     },
58     #[error("Utf8 error for string in {range:?}: {error}\n{error_trace}")]
59     Utf8Error {
60         #[source]
61         error: core::str::Utf8Error,
62         range: Range<usize>,
63         error_trace: ErrorTrace,
64     },
65     #[error("String in range [{}, {}) is missing its null terminator.\n{error_trace}",
66             range.start, range.end)]
67     MissingNullTerminator {
68         range: Range<usize>,
69         error_trace: ErrorTrace,
70     },
71     #[error("Type `{unaligned_type}` at position {position} is unaligned.\n{error_trace}")]
72     Unaligned {
73         position: usize,
74         unaligned_type: &'static str,
75         error_trace: ErrorTrace,
76     },
77     #[error("Range [{}, {}) is out of bounds.\n{error_trace}", range.start, range.end)]
78     RangeOutOfBounds {
79         range: Range<usize>,
80         error_trace: ErrorTrace,
81     },
82     #[error(
83         "Signed offset at position {position} has value {soffset} which points out of bounds.\
84              \n{error_trace}"
85     )]
86     SignedOffsetOutOfBounds {
87         soffset: SOffsetT,
88         position: usize,
89         error_trace: ErrorTrace,
90     },
91     // Dos detecting errors. These do not get error traces since it will probably be very large.
92     #[error("Too many tables.")]
93     TooManyTables,
94     #[error("Apparent size too large.")]
95     ApparentSizeTooLarge,
96     #[error("Nested table depth limit reached.")]
97     DepthLimitReached,
98 }
99 
100 impl core::fmt::Display for ErrorTrace {
fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result101     fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
102         use ErrorTraceDetail::*;
103         for e in self.0.iter() {
104             match e {
105                 VectorElement { index, position } => {
106                     writeln!(
107                         f,
108                         "\twhile verifying vector element {:?} at position {:?}",
109                         index, position
110                     )?;
111                 }
112                 TableField {
113                     field_name,
114                     position,
115                 } => {
116                     writeln!(
117                         f,
118                         "\twhile verifying table field `{}` at position {:?}",
119                         field_name, position
120                     )?;
121                 }
122                 UnionVariant { variant, position } => {
123                     writeln!(
124                         f,
125                         "\t while verifying union variant `{}` at position {:?}",
126                         variant, position
127                     )?;
128                 }
129             }
130         }
131         Ok(())
132     }
133 }
134 
135 pub type Result<T> = core::result::Result<T, InvalidFlatbuffer>;
136 
137 impl InvalidFlatbuffer {
new_range_oob<T>(start: usize, end: usize) -> Result<T>138     fn new_range_oob<T>(start: usize, end: usize) -> Result<T> {
139         Err(Self::RangeOutOfBounds {
140             range: Range { start, end },
141             error_trace: Default::default(),
142         })
143     }
new_inconsistent_union<T>(field: &'static str, field_type: &'static str) -> Result<T>144     fn new_inconsistent_union<T>(field: &'static str, field_type: &'static str) -> Result<T> {
145         Err(Self::InconsistentUnion {
146             field,
147             field_type,
148             error_trace: Default::default(),
149         })
150     }
new_missing_required<T>(required: &'static str) -> Result<T>151     fn new_missing_required<T>(required: &'static str) -> Result<T> {
152         Err(Self::MissingRequiredField {
153             required,
154             error_trace: Default::default(),
155         })
156     }
157 }
158 
159 /// Records the path to the verifier detail if the error is a data error and not a DoS error.
append_trace<T>(mut res: Result<T>, d: ErrorTraceDetail) -> Result<T>160 fn append_trace<T>(mut res: Result<T>, d: ErrorTraceDetail) -> Result<T> {
161     if let Err(e) = res.as_mut() {
162         use InvalidFlatbuffer::*;
163         if let MissingRequiredField { error_trace, .. }
164         | Unaligned { error_trace, .. }
165         | RangeOutOfBounds { error_trace, .. }
166         | InconsistentUnion { error_trace, .. }
167         | Utf8Error { error_trace, .. }
168         | MissingNullTerminator { error_trace, .. }
169         | SignedOffsetOutOfBounds { error_trace, .. } = e
170         {
171             error_trace.0.push(d)
172         }
173     }
174     res
175 }
176 
177 /// Adds a TableField trace detail if `res` is a data error.
trace_field<T>(res: Result<T>, field_name: &'static str, position: usize) -> Result<T>178 fn trace_field<T>(res: Result<T>, field_name: &'static str, position: usize) -> Result<T> {
179     append_trace(
180         res,
181         ErrorTraceDetail::TableField {
182             field_name,
183             position,
184         },
185     )
186 }
187 /// Adds a TableField trace detail if `res` is a data error.
trace_elem<T>(res: Result<T>, index: usize, position: usize) -> Result<T>188 fn trace_elem<T>(res: Result<T>, index: usize, position: usize) -> Result<T> {
189     append_trace(res, ErrorTraceDetail::VectorElement { index, position })
190 }
191 
192 #[derive(Debug, Clone, PartialEq, Eq)]
193 pub struct VerifierOptions {
194     /// Maximum depth of nested tables allowed in a valid flatbuffer.
195     pub max_depth: usize,
196     /// Maximum number of tables allowed in a valid flatbuffer.
197     pub max_tables: usize,
198     /// Maximum "apparent" size of the message if the Flatbuffer object DAG is expanded into a
199     /// tree.
200     pub max_apparent_size: usize,
201     /// Ignore errors where a string is missing its null terminator.
202     /// This is mostly a problem if the message will be sent to a client using old c-strings.
203     pub ignore_missing_null_terminator: bool,
204     // probably want an option to ignore utf8 errors since strings come from c++
205     // options to error un-recognized enums and unions? possible footgun.
206     // Ignore nested flatbuffers, etc?
207 }
208 impl Default for VerifierOptions {
default() -> Self209     fn default() -> Self {
210         Self {
211             max_depth: 64,
212             max_tables: 1_000_000,
213             // size_ might do something different.
214             max_apparent_size: 1 << 31,
215             ignore_missing_null_terminator: false,
216         }
217     }
218 }
219 
220 /// Carries the verification state. Should not be reused between tables.
221 #[derive(Debug)]
222 pub struct Verifier<'opts, 'buf> {
223     buffer: &'buf [u8],
224     opts: &'opts VerifierOptions,
225     depth: usize,
226     num_tables: usize,
227     apparent_size: usize,
228 }
229 impl<'opts, 'buf> Verifier<'opts, 'buf> {
new(opts: &'opts VerifierOptions, buffer: &'buf [u8]) -> Self230     pub fn new(opts: &'opts VerifierOptions, buffer: &'buf [u8]) -> Self {
231         Self {
232             opts,
233             buffer,
234             depth: 0,
235             num_tables: 0,
236             apparent_size: 0,
237         }
238     }
239     /// Resets verifier internal state.
240     #[inline]
reset(&mut self)241     pub fn reset(&mut self) {
242         self.depth = 0;
243         self.num_tables = 0;
244         self.num_tables = 0;
245     }
246     /// Checks `pos` is aligned to T's alignment. This does not mean `buffer[pos]` is aligned w.r.t
247     /// memory since `buffer: &[u8]` has alignment 1.
248     ///
249     /// ### WARNING
250     /// This does not work for flatbuffers-structs as they have alignment 1 according to
251     /// `core::mem::align_of` but are meant to have higher alignment within a Flatbuffer w.r.t.
252     /// `buffer[0]`. TODO(caspern).
253     #[inline]
is_aligned<T>(&self, pos: usize) -> Result<()>254     fn is_aligned<T>(&self, pos: usize) -> Result<()> {
255         if pos % core::mem::align_of::<T>() == 0 {
256             Ok(())
257         } else {
258             Err(InvalidFlatbuffer::Unaligned {
259                 unaligned_type: core::any::type_name::<T>(),
260                 position: pos,
261                 error_trace: Default::default(),
262             })
263         }
264     }
265     #[inline]
range_in_buffer(&mut self, pos: usize, size: usize) -> Result<()>266     fn range_in_buffer(&mut self, pos: usize, size: usize) -> Result<()> {
267         let end = pos.saturating_add(size);
268         if end > self.buffer.len() {
269             return InvalidFlatbuffer::new_range_oob(pos, end);
270         }
271         self.apparent_size += size;
272         if self.apparent_size > self.opts.max_apparent_size {
273             return Err(InvalidFlatbuffer::ApparentSizeTooLarge);
274         }
275         Ok(())
276     }
277     /// Check that there really is a T in there.
278     #[inline]
in_buffer<T>(&mut self, pos: usize) -> Result<()>279     pub fn in_buffer<T>(&mut self, pos: usize) -> Result<()> {
280         self.is_aligned::<T>(pos)?;
281         self.range_in_buffer(pos, core::mem::size_of::<T>())
282     }
283     #[inline]
get_u16(&mut self, pos: usize) -> Result<u16>284     fn get_u16(&mut self, pos: usize) -> Result<u16> {
285         self.in_buffer::<u16>(pos)?;
286         Ok(u16::from_le_bytes([self.buffer[pos], self.buffer[pos + 1]]))
287     }
288     #[inline]
get_uoffset(&mut self, pos: usize) -> Result<UOffsetT>289     fn get_uoffset(&mut self, pos: usize) -> Result<UOffsetT> {
290         self.in_buffer::<u32>(pos)?;
291         Ok(u32::from_le_bytes([
292             self.buffer[pos],
293             self.buffer[pos + 1],
294             self.buffer[pos + 2],
295             self.buffer[pos + 3],
296         ]))
297     }
298     #[inline]
deref_soffset(&mut self, pos: usize) -> Result<usize>299     fn deref_soffset(&mut self, pos: usize) -> Result<usize> {
300         self.in_buffer::<SOffsetT>(pos)?;
301         let offset = SOffsetT::from_le_bytes([
302             self.buffer[pos],
303             self.buffer[pos + 1],
304             self.buffer[pos + 2],
305             self.buffer[pos + 3],
306         ]);
307 
308         // signed offsets are subtracted.
309         let derefed = if offset > 0 {
310             pos.checked_sub(offset.abs() as usize)
311         } else {
312             pos.checked_add(offset.abs() as usize)
313         };
314         if let Some(x) = derefed {
315             if x < self.buffer.len() {
316                 return Ok(x);
317             }
318         }
319         Err(InvalidFlatbuffer::SignedOffsetOutOfBounds {
320             soffset: offset,
321             position: pos,
322             error_trace: Default::default(),
323         })
324     }
325     #[inline]
visit_table<'ver>( &'ver mut self, table_pos: usize, ) -> Result<TableVerifier<'ver, 'opts, 'buf>>326     pub fn visit_table<'ver>(
327         &'ver mut self,
328         table_pos: usize,
329     ) -> Result<TableVerifier<'ver, 'opts, 'buf>> {
330         let vtable_pos = self.deref_soffset(table_pos)?;
331         let vtable_len = self.get_u16(vtable_pos)? as usize;
332         self.is_aligned::<VOffsetT>(vtable_pos.saturating_add(vtable_len))?; // i.e. vtable_len is even.
333         self.range_in_buffer(vtable_pos, vtable_len)?;
334         // Check bounds.
335         self.num_tables += 1;
336         if self.num_tables > self.opts.max_tables {
337             return Err(InvalidFlatbuffer::TooManyTables);
338         }
339         self.depth += 1;
340         if self.depth > self.opts.max_depth {
341             return Err(InvalidFlatbuffer::DepthLimitReached);
342         }
343         Ok(TableVerifier {
344             pos: table_pos,
345             vtable: vtable_pos,
346             vtable_len,
347             verifier: self,
348         })
349     }
350 
351     /// Runs the union variant's type's verifier assuming the variant is at the given position,
352     /// tracing the error.
verify_union_variant<T: Verifiable>( &mut self, variant: &'static str, position: usize, ) -> Result<()>353     pub fn verify_union_variant<T: Verifiable>(
354         &mut self,
355         variant: &'static str,
356         position: usize,
357     ) -> Result<()> {
358         let res = T::run_verifier(self, position);
359         append_trace(res, ErrorTraceDetail::UnionVariant { variant, position })
360     }
361 }
362 
363 // Cache table metadata in usize so we don't have to cast types or jump around so much.
364 // We will visit every field anyway.
365 pub struct TableVerifier<'ver, 'opts, 'buf> {
366     // Absolute position of table in buffer
367     pos: usize,
368     // Absolute position of vtable in buffer.
369     vtable: usize,
370     // Length of vtable.
371     vtable_len: usize,
372     // Verifier struct which holds the surrounding state and options.
373     verifier: &'ver mut Verifier<'opts, 'buf>,
374 }
375 impl<'ver, 'opts, 'buf> TableVerifier<'ver, 'opts, 'buf> {
deref(&mut self, field: VOffsetT) -> Result<Option<usize>>376     fn deref(&mut self, field: VOffsetT) -> Result<Option<usize>> {
377         let field = field as usize;
378         if field < self.vtable_len {
379             let field_offset = self.verifier.get_u16(self.vtable.saturating_add(field))?;
380             if field_offset > 0 {
381                 // Field is present.
382                 let field_pos = self.pos.saturating_add(field_offset as usize);
383                 return Ok(Some(field_pos));
384             }
385         }
386         Ok(None)
387     }
388 
389     #[inline]
visit_field<T: Verifiable>( mut self, field_name: &'static str, field: VOffsetT, required: bool, ) -> Result<Self>390     pub fn visit_field<T: Verifiable>(
391         mut self,
392         field_name: &'static str,
393         field: VOffsetT,
394         required: bool,
395     ) -> Result<Self> {
396         if let Some(field_pos) = self.deref(field)? {
397             trace_field(
398                 T::run_verifier(self.verifier, field_pos),
399                 field_name,
400                 field_pos,
401             )?;
402             return Ok(self);
403         }
404         if required {
405             InvalidFlatbuffer::new_missing_required(field_name)
406         } else {
407             Ok(self)
408         }
409     }
410     #[inline]
411     /// Union verification is complicated. The schemas passes this function the metadata of the
412     /// union's key (discriminant) and value fields, and a callback. The function verifies and
413     /// reads the key, then invokes the callback to perform data-dependent verification.
visit_union<Key, UnionVerifier>( mut self, key_field_name: &'static str, key_field_voff: VOffsetT, val_field_name: &'static str, val_field_voff: VOffsetT, required: bool, verify_union: UnionVerifier, ) -> Result<Self> where Key: Follow<'buf> + Verifiable, UnionVerifier: (core::ops::FnOnce(<Key as Follow<'buf>>::Inner, &mut Verifier, usize) -> Result<()>),414     pub fn visit_union<Key, UnionVerifier>(
415         mut self,
416         key_field_name: &'static str,
417         key_field_voff: VOffsetT,
418         val_field_name: &'static str,
419         val_field_voff: VOffsetT,
420         required: bool,
421         verify_union: UnionVerifier,
422     ) -> Result<Self>
423     where
424         Key: Follow<'buf> + Verifiable,
425         UnionVerifier:
426             (core::ops::FnOnce(<Key as Follow<'buf>>::Inner, &mut Verifier, usize) -> Result<()>),
427         // NOTE: <Key as Follow<'buf>>::Inner == Key
428     {
429         // TODO(caspern): how to trace vtable errors?
430         let val_pos = self.deref(val_field_voff)?;
431         let key_pos = self.deref(key_field_voff)?;
432         match (key_pos, val_pos) {
433             (None, None) => {
434                 if required {
435                     InvalidFlatbuffer::new_missing_required(val_field_name)
436                 } else {
437                     Ok(self)
438                 }
439             }
440             (Some(k), Some(v)) => {
441                 trace_field(Key::run_verifier(self.verifier, k), key_field_name, k)?;
442                 let discriminant = Key::follow(self.verifier.buffer, k);
443                 trace_field(
444                     verify_union(discriminant, self.verifier, v),
445                     val_field_name,
446                     v,
447                 )?;
448                 Ok(self)
449             }
450             _ => InvalidFlatbuffer::new_inconsistent_union(key_field_name, val_field_name),
451         }
452     }
finish(self) -> &'ver mut Verifier<'opts, 'buf>453     pub fn finish(self) -> &'ver mut Verifier<'opts, 'buf> {
454         self.verifier.depth -= 1;
455         self.verifier
456     }
457 }
458 
459 // Needs to be implemented for Tables and maybe structs.
460 // Unions need some special treatment.
461 pub trait Verifiable {
462     /// Runs the verifier for this type, assuming its at position `pos` in the verifier's buffer.
463     /// Should not need to be called directly.
run_verifier(v: &mut Verifier, pos: usize) -> Result<()>464     fn run_verifier(v: &mut Verifier, pos: usize) -> Result<()>;
465 }
466 
467 // Verify the uoffset and then pass verifier to the type being pointed to.
468 impl<T: Verifiable> Verifiable for ForwardsUOffset<T> {
469     #[inline]
run_verifier(v: &mut Verifier, pos: usize) -> Result<()>470     fn run_verifier(v: &mut Verifier, pos: usize) -> Result<()> {
471         let offset = v.get_uoffset(pos)? as usize;
472         let next_pos = offset.saturating_add(pos);
473         T::run_verifier(v, next_pos)
474     }
475 }
476 
477 /// Checks and returns the range containing the flatbuffers vector.
verify_vector_range<T>(v: &mut Verifier, pos: usize) -> Result<core::ops::Range<usize>>478 fn verify_vector_range<T>(v: &mut Verifier, pos: usize) -> Result<core::ops::Range<usize>> {
479     let len = v.get_uoffset(pos)? as usize;
480     let start = pos.saturating_add(SIZE_UOFFSET);
481     v.is_aligned::<T>(start)?;
482     let size = len.saturating_mul(core::mem::size_of::<T>());
483     let end = start.saturating_add(size);
484     v.range_in_buffer(start, size)?;
485     Ok(core::ops::Range { start, end })
486 }
487 
488 pub trait SimpleToVerifyInSlice {}
489 impl SimpleToVerifyInSlice for bool {}
490 impl SimpleToVerifyInSlice for i8 {}
491 impl SimpleToVerifyInSlice for u8 {}
492 impl SimpleToVerifyInSlice for i16 {}
493 impl SimpleToVerifyInSlice for u16 {}
494 impl SimpleToVerifyInSlice for i32 {}
495 impl SimpleToVerifyInSlice for u32 {}
496 impl SimpleToVerifyInSlice for f32 {}
497 impl SimpleToVerifyInSlice for i64 {}
498 impl SimpleToVerifyInSlice for u64 {}
499 impl SimpleToVerifyInSlice for f64 {}
500 
501 impl<T: SimpleToVerifyInSlice> Verifiable for Vector<'_, T> {
run_verifier(v: &mut Verifier, pos: usize) -> Result<()>502     fn run_verifier(v: &mut Verifier, pos: usize) -> Result<()> {
503         verify_vector_range::<T>(v, pos)?;
504         Ok(())
505     }
506 }
507 
508 impl<T: Verifiable> Verifiable for SkipSizePrefix<T> {
509     #[inline]
run_verifier(v: &mut Verifier, pos: usize) -> Result<()>510     fn run_verifier(v: &mut Verifier, pos: usize) -> Result<()> {
511         T::run_verifier(v, pos.saturating_add(crate::SIZE_SIZEPREFIX))
512     }
513 }
514 
515 impl<T: Verifiable> Verifiable for Vector<'_, ForwardsUOffset<T>> {
516     #[inline]
run_verifier(v: &mut Verifier, pos: usize) -> Result<()>517     fn run_verifier(v: &mut Verifier, pos: usize) -> Result<()> {
518         let range = verify_vector_range::<ForwardsUOffset<T>>(v, pos)?;
519         let size = core::mem::size_of::<ForwardsUOffset<T>>();
520         for (i, element_pos) in range.step_by(size).enumerate() {
521             trace_elem(
522                 <ForwardsUOffset<T>>::run_verifier(v, element_pos),
523                 i,
524                 element_pos,
525             )?;
526         }
527         Ok(())
528     }
529 }
530 
531 impl<'a> Verifiable for &'a str {
532     #[inline]
run_verifier(v: &mut Verifier, pos: usize) -> Result<()>533     fn run_verifier(v: &mut Verifier, pos: usize) -> Result<()> {
534         let range = verify_vector_range::<u8>(v, pos)?;
535         let has_null_terminator = v.buffer.get(range.end).map(|&b| b == 0).unwrap_or(false);
536         let s = core::str::from_utf8(&v.buffer[range.clone()]);
537         if let Err(error) = s {
538             return Err(InvalidFlatbuffer::Utf8Error {
539                 error,
540                 range,
541                 error_trace: Default::default(),
542             });
543         }
544         if !v.opts.ignore_missing_null_terminator && !has_null_terminator {
545             return Err(InvalidFlatbuffer::MissingNullTerminator {
546                 range,
547                 error_trace: Default::default(),
548             });
549         }
550         Ok(())
551     }
552 }
553 
554 // Verify VectorOfTables, Unions, Arrays, Structs...
555 macro_rules! impl_verifiable_for {
556     ($T: ty) => {
557         impl Verifiable for $T {
558             #[inline]
559             fn run_verifier<'opts, 'buf>(v: &mut Verifier<'opts, 'buf>, pos: usize) -> Result<()> {
560                 v.in_buffer::<$T>(pos)
561             }
562         }
563     };
564 }
565 impl_verifiable_for!(bool);
566 impl_verifiable_for!(u8);
567 impl_verifiable_for!(i8);
568 impl_verifiable_for!(u16);
569 impl_verifiable_for!(i16);
570 impl_verifiable_for!(u32);
571 impl_verifiable_for!(i32);
572 impl_verifiable_for!(f32);
573 impl_verifiable_for!(u64);
574 impl_verifiable_for!(i64);
575 impl_verifiable_for!(f64);
576