flatbuffers_verifier/
lib.rs

1use 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}