flatbuffers_verifier/
lib.rs1use flatbuffers::{read_scalar, Follow, UOffsetT, SIZE_SIZEPREFIX, SIZE_UOFFSET};
2use std::error;
3use std::fmt;
4use std::result;
5
6#[derive(Debug, Eq, PartialEq)]
7pub enum Error {
8 OutOfBounds,
9 NonNullTerminatedString,
10 UnmatchedUnion,
11}
12
13pub type Result = result::Result<(), Error>;
14
15impl fmt::Display for Error {
16 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
17 match self {
18 Error::OutOfBounds => write!(f, "memory access is out of bounds"),
19 Error::NonNullTerminatedString => write!(f, "string is not terminated with null"),
20 Error::UnmatchedUnion => write!(f, "union type and value does not match"),
21 }
22 }
23}
24
25impl error::Error for Error {}
26
27pub trait Verify {
28 fn verify(&self) -> Result;
29}
30
31pub const MAX_OFFSET_LOC: usize = usize::max_value() - SIZE_UOFFSET;
32
33fn read_uoffset(buf: &[u8], offset_loc: usize) -> usize {
34 read_scalar::<UOffsetT>(&buf[offset_loc..]) as usize
35}
36
37fn try_read_uoffset(buf: &[u8], offset_loc: usize) -> result::Result<usize, Error> {
38 if offset_loc <= MAX_OFFSET_LOC && offset_loc + SIZE_UOFFSET <= buf.len() {
39 Ok(read_uoffset(buf, offset_loc))
40 } else {
41 Err(Error::OutOfBounds)
42 }
43}
44
45pub fn try_follow_uoffset(buf: &[u8], offset_loc: usize) -> result::Result<usize, Error> {
46 try_read_uoffset(buf, offset_loc)
47 .and_then(|offset| offset_loc.checked_add(offset).ok_or(Error::OutOfBounds))
48}
49
50#[allow(dead_code)]
51pub struct StringVerifier<'a> {
52 buf: &'a [u8],
53 loc: usize,
54}
55
56impl<'a> Follow<'a> for StringVerifier<'a> {
57 type Inner = Self;
58 fn follow(buf: &'a [u8], loc: usize) -> Self {
59 Self { buf, loc }
60 }
61}
62
63impl<'a> Verify for StringVerifier<'a> {
64 fn verify(&self) -> Result {
65 let buf_len = self.buf.len();
66
67 let len = try_read_uoffset(self.buf, self.loc)?;
68 let null_loc = (self.loc + SIZE_UOFFSET)
69 .checked_add(len)
70 .ok_or(Error::OutOfBounds)?;
71
72 if null_loc >= buf_len {
73 return Err(Error::OutOfBounds);
74 }
75 if self.buf[null_loc] != 0 {
76 return Err(Error::NonNullTerminatedString);
77 }
78
79 Ok(())
80 }
81}
82
83#[allow(dead_code)]
84pub struct VectorVerifier<'a> {
85 buf: &'a [u8],
86 loc: usize,
87}
88
89impl<'a> Follow<'a> for VectorVerifier<'a> {
90 type Inner = Self;
91 fn follow(buf: &'a [u8], loc: usize) -> Self {
92 Self { buf, loc }
93 }
94}
95
96impl<'a> VectorVerifier<'a> {
97 pub fn verify_scalar_elements(&self, scalar_size: usize) -> Result {
98 let len = try_read_uoffset(self.buf, self.loc)?;
99
100 match (self.loc + SIZE_UOFFSET)
101 .checked_add(len * scalar_size)
102 .filter(|loc| *loc <= self.buf.len())
103 {
104 Some(_) => Ok(()),
105 _ => Err(Error::OutOfBounds),
106 }
107 }
108
109 pub fn verify_reference_elements<E>(&self) -> Result
110 where
111 E: Follow<'a>,
112 <E as Follow<'a>>::Inner: Verify,
113 {
114 let len = try_read_uoffset(self.buf, self.loc)?;
115
116 let mut offset_loc = self.loc + SIZE_UOFFSET;
117 let end_loc = offset_loc
118 .checked_add(len * SIZE_UOFFSET)
119 .ok_or(Error::OutOfBounds)?;
120 if end_loc > self.buf.len() {
121 return Err(Error::OutOfBounds);
122 }
123
124 while offset_loc < end_loc {
125 E::follow(
126 self.buf,
127 offset_loc
128 .checked_add(read_uoffset(self.buf, offset_loc))
129 .ok_or(Error::OutOfBounds)?,
130 )
131 .verify()?;
132 offset_loc += SIZE_UOFFSET;
133 }
134
135 Ok(())
136 }
137}
138
139pub fn get_root<'a, T>(data: &'a [u8]) -> result::Result<T::Inner, Error>
140where
141 T: Follow<'a> + 'a,
142 T::Inner: Verify,
143{
144 if data.len() < SIZE_UOFFSET {
145 return Err(Error::OutOfBounds);
146 }
147
148 let root = flatbuffers::get_root::<T>(data);
149 root.verify()?;
150 Ok(root)
151}
152
153pub fn get_size_prefixed_root<'a, T>(data: &'a [u8]) -> result::Result<T::Inner, Error>
154where
155 T: Follow<'a> + 'a,
156 T::Inner: Verify,
157{
158 if data.len() < SIZE_SIZEPREFIX + SIZE_UOFFSET {
159 return Err(Error::OutOfBounds);
160 }
161
162 let root = flatbuffers::get_size_prefixed_root::<T>(data);
163 root.verify()?;
164 Ok(root)
165}