blob: 36a5775e301c038a2834f95ee762cbd27104fb39 [file] [log] [blame]
James Kuszmaul8e62b022022-03-22 09:33:25 -07001#[cfg(feature = "no_std")]
2use alloc::vec::Vec;
3use core::ops::Range;
4use core::option::Option;
5use crate::follow::Follow;
6use crate::{ForwardsUOffset, SOffsetT, SkipSizePrefix, UOffsetT, VOffsetT, Vector, SIZE_UOFFSET};
7
8#[cfg(feature="no_std")]
Austin Schuh2dd86a92022-09-14 21:19:23 -07009use thiserror_core2::Error;
10#[cfg(not(feature="no_std"))]
James Kuszmaul8e62b022022-03-22 09:33:25 -070011use 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)]
17pub 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)]
32pub struct ErrorTrace(Vec<ErrorTraceDetail>);
33impl core::convert::AsRef<[ErrorTraceDetail]> for ErrorTrace {
34 #[inline]
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)]
43pub 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
100impl core::fmt::Display for ErrorTrace {
101 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
135pub type Result<T> = core::result::Result<T, InvalidFlatbuffer>;
136
137impl InvalidFlatbuffer {
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 }
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 }
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.
160fn 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.
178fn 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.
188fn 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)]
193pub 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}
208impl Default for VerifierOptions {
209 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)]
222pub struct Verifier<'opts, 'buf> {
223 buffer: &'buf [u8],
224 opts: &'opts VerifierOptions,
225 depth: usize,
226 num_tables: usize,
227 apparent_size: usize,
228}
229impl<'opts, 'buf> Verifier<'opts, 'buf> {
230 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]
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]
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]
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]
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]
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]
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]
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]
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.
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.
365pub 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}
375impl<'ver, 'opts, 'buf> TableVerifier<'ver, 'opts, 'buf> {
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]
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.
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 }
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.
461pub 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.
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.
468impl<T: Verifiable> Verifiable for ForwardsUOffset<T> {
469 #[inline]
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.
478fn 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
488pub trait SimpleToVerifyInSlice {}
489impl SimpleToVerifyInSlice for bool {}
490impl SimpleToVerifyInSlice for i8 {}
491impl SimpleToVerifyInSlice for u8 {}
492impl SimpleToVerifyInSlice for i16 {}
493impl SimpleToVerifyInSlice for u16 {}
494impl SimpleToVerifyInSlice for i32 {}
495impl SimpleToVerifyInSlice for u32 {}
496impl SimpleToVerifyInSlice for f32 {}
497impl SimpleToVerifyInSlice for i64 {}
498impl SimpleToVerifyInSlice for u64 {}
499impl SimpleToVerifyInSlice for f64 {}
500
501impl<T: SimpleToVerifyInSlice> Verifiable for Vector<'_, T> {
502 fn run_verifier(v: &mut Verifier, pos: usize) -> Result<()> {
503 verify_vector_range::<T>(v, pos)?;
504 Ok(())
505 }
506}
507
508impl<T: Verifiable> Verifiable for SkipSizePrefix<T> {
509 #[inline]
510 fn run_verifier(v: &mut Verifier, pos: usize) -> Result<()> {
511 T::run_verifier(v, pos.saturating_add(crate::SIZE_SIZEPREFIX))
512 }
513}
514
515impl<T: Verifiable> Verifiable for Vector<'_, ForwardsUOffset<T>> {
516 #[inline]
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
531impl<'a> Verifiable for &'a str {
532 #[inline]
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...
555macro_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}
565impl_verifiable_for!(bool);
566impl_verifiable_for!(u8);
567impl_verifiable_for!(i8);
568impl_verifiable_for!(u16);
569impl_verifiable_for!(i16);
570impl_verifiable_for!(u32);
571impl_verifiable_for!(i32);
572impl_verifiable_for!(f32);
573impl_verifiable_for!(u64);
574impl_verifiable_for!(i64);
575impl_verifiable_for!(f64);