From c251109215699b8fcfe3197de08249c24a4c4b44 Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 1 Apr 2019 10:38:44 +0800 Subject: [PATCH 1/3] fix: flatbuffers offset overflow Fix overflow panic when following the offset in malformed flatbuffers. Refs https://github.com/nervosnetwork/cfb/pull/15 --- Cargo.toml | 1 + Makefile | 29 ++ protocols/discovery/Cargo.toml | 3 +- protocols/discovery/src/protocol.rs | 12 +- .../src/protocol_generated_verifier.rs | 418 +++++++++--------- protocols/identify/Cargo.toml | 3 +- protocols/identify/src/protocol.rs | 13 +- .../src/protocol_generated_verifier.rs | 344 +++++++------- protocols/ping/Cargo.toml | 1 + protocols/ping/src/lib.rs | 3 +- .../ping/src/protocol_generated_verifier.rs | 288 +++++------- secio/Cargo.toml | 1 + .../handshake/handshake_generated_verifier.rs | 334 +++++++------- secio/src/handshake/handshake_struct.rs | 10 +- src/protocol_select/mod.rs | 8 +- .../protocol_select_generated_verifier.rs | 198 +++------ 16 files changed, 770 insertions(+), 896 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7ea94fbd..80820bdf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ bytes = "0.4" tokio-threadpool = "0.1" flatbuffers = "0.5.0" +flatbuffers-verifier = "0.1.8" multiaddr = { package = "parity-multiaddr", version = "0.3.0" } [dev-dependencies] diff --git a/Makefile b/Makefile index e73355eb..f20c2612 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,15 @@ +FLATC := flatc +CFBC := cfbc + +FBS_FILES := \ + src/protocol_select/protocol_select.fbs \ + secio/src/handshake/handshake.fbs \ + protocols/identify/src/protocol.fbs \ + protocols/ping/src/protocol.fbs \ + protocols/discovery/src/protocol.fbs + +FLATC_RUST_FILES := $(patsubst %.fbs,%_generated.rs,${FBS_FILES}) +FLATBUFFERS_VERIFIER_FILES := $(patsubst %.fbs,%_generated_verifier.rs,${FBS_FILES}) fmt: cargo fmt --all -- --check @@ -13,3 +25,20 @@ examples: ci: fmt clippy test examples git diff --exit-code Cargo.lock + +%_generated_verifier.rs: %.fbs + $(FLATC) -b --schema -o $(shell dirname $@) $< + $(CFBC) -o $(shell dirname $@) $*.bfbs + rm -f $*_builder.rs $*.bfbs + +%_generated.rs: %.fbs + $(FLATC) -r -o $(shell dirname $@) $< + +gen-fb: $(FLATC_RUST_FILES) $(FLATBUFFERS_VERIFIER_FILES) + +clean-fb: + rm -f $(FLATC_RUST_FILES) $(FLATBUFFERS_VERIFIER_FILES) + + + +.PHONY: fmt clippy test examples ci gen-fb clean-fb diff --git a/protocols/discovery/Cargo.toml b/protocols/discovery/Cargo.toml index 05b69e07..f67f2a67 100644 --- a/protocols/discovery/Cargo.toml +++ b/protocols/discovery/Cargo.toml @@ -22,6 +22,7 @@ serde_derive = "1.0" trust-dns = "0.15" rand = "0.6.1" flatbuffers = "0.5.0" +flatbuffers-verifier = "0.1.8" [dev-dependencies] -env_logger = "0.6" \ No newline at end of file +env_logger = "0.6" diff --git a/protocols/discovery/src/protocol.rs b/protocols/discovery/src/protocol.rs index 65988cca..dbff2b95 100644 --- a/protocols/discovery/src/protocol.rs +++ b/protocols/discovery/src/protocol.rs @@ -2,18 +2,16 @@ use std::io; use bytes::{Bytes, BytesMut}; use flatbuffers::FlatBufferBuilder; +use flatbuffers_verifier::get_root; use log::debug; use p2p::multiaddr::Multiaddr; use tokio::codec::length_delimited::LengthDelimitedCodec; use tokio::codec::{Decoder, Encoder}; -use crate::{ - protocol_generated::p2p::discovery::{ - BytesBuilder, DiscoveryMessage as FbsDiscoveryMessage, DiscoveryMessageBuilder, - DiscoveryPayload as FbsDiscoveryPayload, GetNodes as FbsGetNodes, GetNodesBuilder, - NodeBuilder, Nodes as FbsNodes, NodesBuilder, - }, - protocol_generated_verifier::get_root, +use crate::protocol_generated::p2p::discovery::{ + BytesBuilder, DiscoveryMessage as FbsDiscoveryMessage, DiscoveryMessageBuilder, + DiscoveryPayload as FbsDiscoveryPayload, GetNodes as FbsGetNodes, GetNodesBuilder, NodeBuilder, + Nodes as FbsNodes, NodesBuilder, }; pub(crate) struct DiscoveryCodec { diff --git a/protocols/discovery/src/protocol_generated_verifier.rs b/protocols/discovery/src/protocol_generated_verifier.rs index 7bbc59b3..2c38dd85 100644 --- a/protocols/discovery/src/protocol_generated_verifier.rs +++ b/protocols/discovery/src/protocol_generated_verifier.rs @@ -1,151 +1,24 @@ //! This file is auto-generated by cfbc. use super::protocol_generated as reader; -use flatbuffers; -use std::error; -use std::fmt; -use std::result; - -#[derive(Debug, Eq, PartialEq)] -pub enum Error { - OutOfBounds, - NonNullTerminatedString, - UnmatchedUnion, -} - -pub type Result = result::Result<(), Error>; - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::OutOfBounds => write!(f, "memory access is out of bounds"), - Error::NonNullTerminatedString => write!(f, "string is not terminated with null"), - Error::UnmatchedUnion => write!(f, "union type and value does not match"), - } - } -} - -impl error::Error for Error {} - -pub trait Verify { - fn verify(&self) -> Result; -} - -fn read_uoffset(buf: &[u8], offset_loc: usize) -> usize { - flatbuffers::read_scalar::(&buf[offset_loc..]) as usize -} - -fn try_read_uoffset(buf: &[u8], offset_loc: usize) -> result::Result { - if offset_loc + flatbuffers::SIZE_UOFFSET <= buf.len() { - Ok(read_uoffset(buf, offset_loc)) - } else { - Err(Error::OutOfBounds) - } -} - -pub fn try_follow_uoffset(buf: &[u8], offset_loc: usize) -> result::Result { - try_read_uoffset(buf, offset_loc).map(|offset| offset_loc + offset) -} - -pub struct StringVerifier<'a> { - buf: &'a [u8], - loc: usize, -} - -impl<'a> flatbuffers::Follow<'a> for StringVerifier<'a> { - type Inner = Self; - fn follow(buf: &'a [u8], loc: usize) -> Self { - Self { buf, loc } - } -} - -impl<'a> Verify for StringVerifier<'a> { - fn verify(&self) -> Result { - let buf_len = self.buf.len(); - - let len = try_read_uoffset(self.buf, self.loc)?; - if self.loc + flatbuffers::SIZE_UOFFSET + len + 1 > buf_len { - return Err(Error::OutOfBounds); - } - - if self.buf[self.loc + flatbuffers::SIZE_UOFFSET + len] != 0 { - return Err(Error::NonNullTerminatedString); - } - - Ok(()) - } -} - -pub struct VectorVerifier<'a> { - buf: &'a [u8], - loc: usize, -} - -impl<'a> flatbuffers::Follow<'a> for VectorVerifier<'a> { - type Inner = Self; - fn follow(buf: &'a [u8], loc: usize) -> Self { - Self { buf, loc } - } -} - -impl<'a> VectorVerifier<'a> { - pub fn verify_scalar_elements(&self, scalar_size: usize) -> Result { - let len = try_read_uoffset(self.buf, self.loc)?; - - if self.loc + flatbuffers::SIZE_UOFFSET + len * scalar_size > self.buf.len() { - return Err(Error::OutOfBounds); - } - - Ok(()) - } - - pub fn verify_reference_elements(&self) -> Result - where - E: flatbuffers::Follow<'a>, - >::Inner: Verify, - { - let len = try_read_uoffset(self.buf, self.loc)?; - - let mut offset_loc = self.loc + flatbuffers::SIZE_UOFFSET; - let end_loc = offset_loc + len * flatbuffers::SIZE_UOFFSET; - if end_loc > self.buf.len() { - return Err(Error::OutOfBounds); - } - - while offset_loc < end_loc { - E::follow(self.buf, offset_loc + read_uoffset(self.buf, offset_loc)).verify()?; - offset_loc += flatbuffers::SIZE_UOFFSET; - } - - Ok(()) - } -} - -pub fn get_root<'a, T>(data: &'a [u8]) -> result::Result -where - T: flatbuffers::Follow<'a> + 'a, - T::Inner: Verify, -{ - if data.len() < flatbuffers::SIZE_UOFFSET { - return Err(Error::OutOfBounds); - } - - let root = flatbuffers::get_root::(data); - root.verify()?; - Ok(root) -} pub mod p2p { #![allow(unused_imports)] use super::reader::p2p as reader; - pub use super::{try_follow_uoffset, Error, Result, StringVerifier, VectorVerifier, Verify}; use flatbuffers::{self, Follow}; + use flatbuffers_verifier::{ + try_follow_uoffset, Error, Result, StringVerifier, VectorVerifier, Verify, + MAX_OFFSET_LOC, + }; pub mod discovery { #![allow(unused_imports)] use super::reader::discovery as reader; - pub use super::{try_follow_uoffset, Error, Result, StringVerifier, VectorVerifier, Verify}; use flatbuffers::{self, Follow}; + use flatbuffers_verifier::{ + try_follow_uoffset, Error, Result, StringVerifier, VectorVerifier, Verify, + MAX_OFFSET_LOC, + }; impl<'a> Verify for reader::Bytes<'a> { fn verify(&self) -> Result { @@ -153,26 +26,51 @@ pub mod p2p { let buf = tab.buf; let buf_len = buf.len(); - if tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { + if tab.loc > MAX_OFFSET_LOC || tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { return Err(Error::OutOfBounds); } let vtab_loc = { - let soffset_slice = &buf[tab.loc..tab.loc + flatbuffers::SIZE_SOFFSET]; + let soffset_slice = &buf[tab.loc..]; let soffset = flatbuffers::read_scalar::(soffset_slice); - (tab.loc as flatbuffers::SOffsetT - soffset) as usize - }; - if vtab_loc + flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET > buf_len { + if soffset >= 0 { + tab.loc.checked_sub(soffset as usize) + } else { + soffset + .checked_neg() + .and_then(|foffset| tab.loc.checked_add(foffset as usize)) + } + } + .ok_or(Error::OutOfBounds)?; + if vtab_loc + .checked_add(flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } let vtab = tab.vtable(); let vtab_num_bytes = vtab.num_bytes(); - if vtab_loc + vtab_num_bytes > buf_len { + let object_inline_num_bytes = vtab.object_inline_num_bytes(); + if vtab_num_bytes < flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET + || object_inline_num_bytes < flatbuffers::SIZE_SOFFSET + { return Err(Error::OutOfBounds); } - let object_inline_num_bytes = vtab.object_inline_num_bytes(); - if tab.loc + object_inline_num_bytes > buf_len { + if vtab_loc + .checked_add(vtab_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { + return Err(Error::OutOfBounds); + } + if tab + .loc + .checked_add(object_inline_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } @@ -185,15 +83,19 @@ pub mod p2p { } } - if Self::VT_SEQ as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_SEQ as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_SEQ) as usize; if voffset > 0 { if voffset + 4 > object_inline_num_bytes { return Err(Error::OutOfBounds); } - let seq_verifier = - VectorVerifier::follow(buf, try_follow_uoffset(buf, tab.loc + voffset)?); + let seq_verifier = VectorVerifier::follow( + buf, + try_follow_uoffset(buf, tab.loc + voffset)?, + ); seq_verifier.verify_scalar_elements(1)?; } } @@ -208,26 +110,51 @@ pub mod p2p { let buf = tab.buf; let buf_len = buf.len(); - if tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { + if tab.loc > MAX_OFFSET_LOC || tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { return Err(Error::OutOfBounds); } let vtab_loc = { - let soffset_slice = &buf[tab.loc..tab.loc + flatbuffers::SIZE_SOFFSET]; + let soffset_slice = &buf[tab.loc..]; let soffset = flatbuffers::read_scalar::(soffset_slice); - (tab.loc as flatbuffers::SOffsetT - soffset) as usize - }; - if vtab_loc + flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET > buf_len { + if soffset >= 0 { + tab.loc.checked_sub(soffset as usize) + } else { + soffset + .checked_neg() + .and_then(|foffset| tab.loc.checked_add(foffset as usize)) + } + } + .ok_or(Error::OutOfBounds)?; + if vtab_loc + .checked_add(flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } let vtab = tab.vtable(); let vtab_num_bytes = vtab.num_bytes(); - if vtab_loc + vtab_num_bytes > buf_len { + let object_inline_num_bytes = vtab.object_inline_num_bytes(); + if vtab_num_bytes < flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET + || object_inline_num_bytes < flatbuffers::SIZE_SOFFSET + { return Err(Error::OutOfBounds); } - let object_inline_num_bytes = vtab.object_inline_num_bytes(); - if tab.loc + object_inline_num_bytes > buf_len { + if vtab_loc + .checked_add(vtab_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { + return Err(Error::OutOfBounds); + } + if tab + .loc + .checked_add(object_inline_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } @@ -240,14 +167,18 @@ pub mod p2p { } } - if Self::VT_PAYLOAD_TYPE as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_PAYLOAD_TYPE as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_PAYLOAD_TYPE) as usize; - if voffset > 0 && voffset + 1 > object_inline_num_bytes { + if voffset > 0 && object_inline_num_bytes - voffset < 1 { return Err(Error::OutOfBounds); } } - if Self::VT_PAYLOAD as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_PAYLOAD as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_PAYLOAD) as usize; if voffset > 0 { if voffset + 4 > object_inline_num_bytes { @@ -278,26 +209,51 @@ pub mod p2p { let buf = tab.buf; let buf_len = buf.len(); - if tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { + if tab.loc > MAX_OFFSET_LOC || tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { return Err(Error::OutOfBounds); } let vtab_loc = { - let soffset_slice = &buf[tab.loc..tab.loc + flatbuffers::SIZE_SOFFSET]; + let soffset_slice = &buf[tab.loc..]; let soffset = flatbuffers::read_scalar::(soffset_slice); - (tab.loc as flatbuffers::SOffsetT - soffset) as usize - }; - if vtab_loc + flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET > buf_len { + if soffset >= 0 { + tab.loc.checked_sub(soffset as usize) + } else { + soffset + .checked_neg() + .and_then(|foffset| tab.loc.checked_add(foffset as usize)) + } + } + .ok_or(Error::OutOfBounds)?; + if vtab_loc + .checked_add(flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } let vtab = tab.vtable(); let vtab_num_bytes = vtab.num_bytes(); - if vtab_loc + vtab_num_bytes > buf_len { + let object_inline_num_bytes = vtab.object_inline_num_bytes(); + if vtab_num_bytes < flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET + || object_inline_num_bytes < flatbuffers::SIZE_SOFFSET + { return Err(Error::OutOfBounds); } - let object_inline_num_bytes = vtab.object_inline_num_bytes(); - if tab.loc + object_inline_num_bytes > buf_len { + if vtab_loc + .checked_add(vtab_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { + return Err(Error::OutOfBounds); + } + if tab + .loc + .checked_add(object_inline_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } @@ -310,23 +266,29 @@ pub mod p2p { } } - if Self::VT_VERSION as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_VERSION as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_VERSION) as usize; - if voffset > 0 && voffset + 4 > object_inline_num_bytes { + if voffset > 0 && object_inline_num_bytes - voffset < 4 { return Err(Error::OutOfBounds); } } - if Self::VT_COUNT as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_COUNT as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_COUNT) as usize; - if voffset > 0 && voffset + 4 > object_inline_num_bytes { + if voffset > 0 && object_inline_num_bytes - voffset < 4 { return Err(Error::OutOfBounds); } } - if Self::VT_LISTEN_PORT as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_LISTEN_PORT as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_LISTEN_PORT) as usize; - if voffset > 0 && voffset + 2 > object_inline_num_bytes { + if voffset > 0 && object_inline_num_bytes - voffset < 2 { return Err(Error::OutOfBounds); } } @@ -341,26 +303,51 @@ pub mod p2p { let buf = tab.buf; let buf_len = buf.len(); - if tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { + if tab.loc > MAX_OFFSET_LOC || tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { return Err(Error::OutOfBounds); } let vtab_loc = { - let soffset_slice = &buf[tab.loc..tab.loc + flatbuffers::SIZE_SOFFSET]; + let soffset_slice = &buf[tab.loc..]; let soffset = flatbuffers::read_scalar::(soffset_slice); - (tab.loc as flatbuffers::SOffsetT - soffset) as usize - }; - if vtab_loc + flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET > buf_len { + if soffset >= 0 { + tab.loc.checked_sub(soffset as usize) + } else { + soffset + .checked_neg() + .and_then(|foffset| tab.loc.checked_add(foffset as usize)) + } + } + .ok_or(Error::OutOfBounds)?; + if vtab_loc + .checked_add(flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } let vtab = tab.vtable(); let vtab_num_bytes = vtab.num_bytes(); - if vtab_loc + vtab_num_bytes > buf_len { + let object_inline_num_bytes = vtab.object_inline_num_bytes(); + if vtab_num_bytes < flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET + || object_inline_num_bytes < flatbuffers::SIZE_SOFFSET + { return Err(Error::OutOfBounds); } - let object_inline_num_bytes = vtab.object_inline_num_bytes(); - if tab.loc + object_inline_num_bytes > buf_len { + if vtab_loc + .checked_add(vtab_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { + return Err(Error::OutOfBounds); + } + if tab + .loc + .checked_add(object_inline_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } @@ -373,16 +360,21 @@ pub mod p2p { } } - if Self::VT_ADDRESSES as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_ADDRESSES as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_ADDRESSES) as usize; if voffset > 0 { if voffset + 4 > object_inline_num_bytes { return Err(Error::OutOfBounds); } - let addresses_verifier = - VectorVerifier::follow(buf, try_follow_uoffset(buf, tab.loc + voffset)?); - addresses_verifier.verify_reference_elements::()?; + let addresses_verifier = VectorVerifier::follow( + buf, + try_follow_uoffset(buf, tab.loc + voffset)?, + ); + addresses_verifier + .verify_reference_elements::()?; } } @@ -396,26 +388,51 @@ pub mod p2p { let buf = tab.buf; let buf_len = buf.len(); - if tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { + if tab.loc > MAX_OFFSET_LOC || tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { return Err(Error::OutOfBounds); } let vtab_loc = { - let soffset_slice = &buf[tab.loc..tab.loc + flatbuffers::SIZE_SOFFSET]; + let soffset_slice = &buf[tab.loc..]; let soffset = flatbuffers::read_scalar::(soffset_slice); - (tab.loc as flatbuffers::SOffsetT - soffset) as usize - }; - if vtab_loc + flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET > buf_len { + if soffset >= 0 { + tab.loc.checked_sub(soffset as usize) + } else { + soffset + .checked_neg() + .and_then(|foffset| tab.loc.checked_add(foffset as usize)) + } + } + .ok_or(Error::OutOfBounds)?; + if vtab_loc + .checked_add(flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } let vtab = tab.vtable(); let vtab_num_bytes = vtab.num_bytes(); - if vtab_loc + vtab_num_bytes > buf_len { + let object_inline_num_bytes = vtab.object_inline_num_bytes(); + if vtab_num_bytes < flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET + || object_inline_num_bytes < flatbuffers::SIZE_SOFFSET + { return Err(Error::OutOfBounds); } - let object_inline_num_bytes = vtab.object_inline_num_bytes(); - if tab.loc + object_inline_num_bytes > buf_len { + if vtab_loc + .checked_add(vtab_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { + return Err(Error::OutOfBounds); + } + if tab + .loc + .checked_add(object_inline_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } @@ -428,23 +445,30 @@ pub mod p2p { } } - if Self::VT_ANNOUNCE as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_ANNOUNCE as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_ANNOUNCE) as usize; - if voffset > 0 && voffset + 1 > object_inline_num_bytes { + if voffset > 0 && object_inline_num_bytes - voffset < 1 { return Err(Error::OutOfBounds); } } - if Self::VT_ITEMS as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_ITEMS as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_ITEMS) as usize; if voffset > 0 { if voffset + 4 > object_inline_num_bytes { return Err(Error::OutOfBounds); } - let items_verifier = - VectorVerifier::follow(buf, try_follow_uoffset(buf, tab.loc + voffset)?); - items_verifier.verify_reference_elements::()?; + let items_verifier = VectorVerifier::follow( + buf, + try_follow_uoffset(buf, tab.loc + voffset)?, + ); + items_verifier + .verify_reference_elements::()?; } } diff --git a/protocols/identify/Cargo.toml b/protocols/identify/Cargo.toml index 0c3ce6c1..3e8b95c4 100644 --- a/protocols/identify/Cargo.toml +++ b/protocols/identify/Cargo.toml @@ -13,5 +13,6 @@ edition = "2018" p2p = { path = "../..", version = "0.2.0-alpha.1", package = "tentacle" } bytes = "0.4" flatbuffers = "0.5.0" +flatbuffers-verifier = "0.1.8" tokio = "0.1" -log = "0.4" \ No newline at end of file +log = "0.4" diff --git a/protocols/identify/src/protocol.rs b/protocols/identify/src/protocol.rs index a4ce8a22..006ddd27 100644 --- a/protocols/identify/src/protocol.rs +++ b/protocols/identify/src/protocol.rs @@ -1,13 +1,10 @@ use flatbuffers::{FlatBufferBuilder, WIPOffset}; +use flatbuffers_verifier::get_root; -use crate::{ - protocol_generated::p2p::identify::{ - Address as FbsAddress, AddressBuilder, IdentifyMessage as FbsIdentifyMessage, - IdentifyMessageBuilder, IdentifyPayload as FbsIdentifyPayload, - ListenAddrs as FbsListenAddrs, ListenAddrsBuilder, ObservedAddr as FbsObservedAddr, - ObservedAddrBuilder, - }, - protocol_generated_verifier::get_root, +use crate::protocol_generated::p2p::identify::{ + Address as FbsAddress, AddressBuilder, IdentifyMessage as FbsIdentifyMessage, + IdentifyMessageBuilder, IdentifyPayload as FbsIdentifyPayload, ListenAddrs as FbsListenAddrs, + ListenAddrsBuilder, ObservedAddr as FbsObservedAddr, ObservedAddrBuilder, }; use p2p::multiaddr::Multiaddr; diff --git a/protocols/identify/src/protocol_generated_verifier.rs b/protocols/identify/src/protocol_generated_verifier.rs index 03c72ccc..8f79a213 100644 --- a/protocols/identify/src/protocol_generated_verifier.rs +++ b/protocols/identify/src/protocol_generated_verifier.rs @@ -1,151 +1,24 @@ //! This file is auto-generated by cfbc. use super::protocol_generated as reader; -use flatbuffers; -use std::error; -use std::fmt; -use std::result; - -#[derive(Debug, Eq, PartialEq)] -pub enum Error { - OutOfBounds, - NonNullTerminatedString, - UnmatchedUnion, -} - -pub type Result = result::Result<(), Error>; - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::OutOfBounds => write!(f, "memory access is out of bounds"), - Error::NonNullTerminatedString => write!(f, "string is not terminated with null"), - Error::UnmatchedUnion => write!(f, "union type and value does not match"), - } - } -} - -impl error::Error for Error {} - -pub trait Verify { - fn verify(&self) -> Result; -} - -fn read_uoffset(buf: &[u8], offset_loc: usize) -> usize { - flatbuffers::read_scalar::(&buf[offset_loc..]) as usize -} - -fn try_read_uoffset(buf: &[u8], offset_loc: usize) -> result::Result { - if offset_loc + flatbuffers::SIZE_UOFFSET <= buf.len() { - Ok(read_uoffset(buf, offset_loc)) - } else { - Err(Error::OutOfBounds) - } -} - -pub fn try_follow_uoffset(buf: &[u8], offset_loc: usize) -> result::Result { - try_read_uoffset(buf, offset_loc).map(|offset| offset_loc + offset) -} - -pub struct StringVerifier<'a> { - buf: &'a [u8], - loc: usize, -} - -impl<'a> flatbuffers::Follow<'a> for StringVerifier<'a> { - type Inner = Self; - fn follow(buf: &'a [u8], loc: usize) -> Self { - Self { buf, loc } - } -} - -impl<'a> Verify for StringVerifier<'a> { - fn verify(&self) -> Result { - let buf_len = self.buf.len(); - - let len = try_read_uoffset(self.buf, self.loc)?; - if self.loc + flatbuffers::SIZE_UOFFSET + len + 1 > buf_len { - return Err(Error::OutOfBounds); - } - - if self.buf[self.loc + flatbuffers::SIZE_UOFFSET + len] != 0 { - return Err(Error::NonNullTerminatedString); - } - - Ok(()) - } -} - -pub struct VectorVerifier<'a> { - buf: &'a [u8], - loc: usize, -} - -impl<'a> flatbuffers::Follow<'a> for VectorVerifier<'a> { - type Inner = Self; - fn follow(buf: &'a [u8], loc: usize) -> Self { - Self { buf, loc } - } -} - -impl<'a> VectorVerifier<'a> { - pub fn verify_scalar_elements(&self, scalar_size: usize) -> Result { - let len = try_read_uoffset(self.buf, self.loc)?; - - if self.loc + flatbuffers::SIZE_UOFFSET + len * scalar_size > self.buf.len() { - return Err(Error::OutOfBounds); - } - - Ok(()) - } - - pub fn verify_reference_elements(&self) -> Result - where - E: flatbuffers::Follow<'a>, - >::Inner: Verify, - { - let len = try_read_uoffset(self.buf, self.loc)?; - - let mut offset_loc = self.loc + flatbuffers::SIZE_UOFFSET; - let end_loc = offset_loc + len * flatbuffers::SIZE_UOFFSET; - if end_loc > self.buf.len() { - return Err(Error::OutOfBounds); - } - - while offset_loc < end_loc { - E::follow(self.buf, offset_loc + read_uoffset(self.buf, offset_loc)).verify()?; - offset_loc += flatbuffers::SIZE_UOFFSET; - } - - Ok(()) - } -} - -pub fn get_root<'a, T>(data: &'a [u8]) -> result::Result -where - T: flatbuffers::Follow<'a> + 'a, - T::Inner: Verify, -{ - if data.len() < flatbuffers::SIZE_UOFFSET { - return Err(Error::OutOfBounds); - } - - let root = flatbuffers::get_root::(data); - root.verify()?; - Ok(root) -} pub mod p2p { #![allow(unused_imports)] use super::reader::p2p as reader; - pub use super::{try_follow_uoffset, Error, Result, StringVerifier, VectorVerifier, Verify}; use flatbuffers::{self, Follow}; + use flatbuffers_verifier::{ + try_follow_uoffset, Error, Result, StringVerifier, VectorVerifier, Verify, + MAX_OFFSET_LOC, + }; pub mod identify { #![allow(unused_imports)] use super::reader::identify as reader; - pub use super::{try_follow_uoffset, Error, Result, StringVerifier, VectorVerifier, Verify}; use flatbuffers::{self, Follow}; + use flatbuffers_verifier::{ + try_follow_uoffset, Error, Result, StringVerifier, VectorVerifier, Verify, + MAX_OFFSET_LOC, + }; impl<'a> Verify for reader::Address<'a> { fn verify(&self) -> Result { @@ -153,26 +26,51 @@ pub mod p2p { let buf = tab.buf; let buf_len = buf.len(); - if tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { + if tab.loc > MAX_OFFSET_LOC || tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { return Err(Error::OutOfBounds); } let vtab_loc = { - let soffset_slice = &buf[tab.loc..tab.loc + flatbuffers::SIZE_SOFFSET]; + let soffset_slice = &buf[tab.loc..]; let soffset = flatbuffers::read_scalar::(soffset_slice); - (tab.loc as flatbuffers::SOffsetT - soffset) as usize - }; - if vtab_loc + flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET > buf_len { + if soffset >= 0 { + tab.loc.checked_sub(soffset as usize) + } else { + soffset + .checked_neg() + .and_then(|foffset| tab.loc.checked_add(foffset as usize)) + } + } + .ok_or(Error::OutOfBounds)?; + if vtab_loc + .checked_add(flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } let vtab = tab.vtable(); let vtab_num_bytes = vtab.num_bytes(); - if vtab_loc + vtab_num_bytes > buf_len { + let object_inline_num_bytes = vtab.object_inline_num_bytes(); + if vtab_num_bytes < flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET + || object_inline_num_bytes < flatbuffers::SIZE_SOFFSET + { return Err(Error::OutOfBounds); } - let object_inline_num_bytes = vtab.object_inline_num_bytes(); - if tab.loc + object_inline_num_bytes > buf_len { + if vtab_loc + .checked_add(vtab_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { + return Err(Error::OutOfBounds); + } + if tab + .loc + .checked_add(object_inline_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } @@ -185,15 +83,19 @@ pub mod p2p { } } - if Self::VT_BYTES as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_BYTES as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_BYTES) as usize; if voffset > 0 { if voffset + 4 > object_inline_num_bytes { return Err(Error::OutOfBounds); } - let bytes_verifier = - VectorVerifier::follow(buf, try_follow_uoffset(buf, tab.loc + voffset)?); + let bytes_verifier = VectorVerifier::follow( + buf, + try_follow_uoffset(buf, tab.loc + voffset)?, + ); bytes_verifier.verify_scalar_elements(1)?; } } @@ -208,26 +110,51 @@ pub mod p2p { let buf = tab.buf; let buf_len = buf.len(); - if tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { + if tab.loc > MAX_OFFSET_LOC || tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { return Err(Error::OutOfBounds); } let vtab_loc = { - let soffset_slice = &buf[tab.loc..tab.loc + flatbuffers::SIZE_SOFFSET]; + let soffset_slice = &buf[tab.loc..]; let soffset = flatbuffers::read_scalar::(soffset_slice); - (tab.loc as flatbuffers::SOffsetT - soffset) as usize - }; - if vtab_loc + flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET > buf_len { + if soffset >= 0 { + tab.loc.checked_sub(soffset as usize) + } else { + soffset + .checked_neg() + .and_then(|foffset| tab.loc.checked_add(foffset as usize)) + } + } + .ok_or(Error::OutOfBounds)?; + if vtab_loc + .checked_add(flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } let vtab = tab.vtable(); let vtab_num_bytes = vtab.num_bytes(); - if vtab_loc + vtab_num_bytes > buf_len { + let object_inline_num_bytes = vtab.object_inline_num_bytes(); + if vtab_num_bytes < flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET + || object_inline_num_bytes < flatbuffers::SIZE_SOFFSET + { return Err(Error::OutOfBounds); } - let object_inline_num_bytes = vtab.object_inline_num_bytes(); - if tab.loc + object_inline_num_bytes > buf_len { + if vtab_loc + .checked_add(vtab_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { + return Err(Error::OutOfBounds); + } + if tab + .loc + .checked_add(object_inline_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } @@ -240,14 +167,18 @@ pub mod p2p { } } - if Self::VT_PAYLOAD_TYPE as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_PAYLOAD_TYPE as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_PAYLOAD_TYPE) as usize; - if voffset > 0 && voffset + 1 > object_inline_num_bytes { + if voffset > 0 && object_inline_num_bytes - voffset < 1 { return Err(Error::OutOfBounds); } } - if Self::VT_PAYLOAD as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_PAYLOAD as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_PAYLOAD) as usize; if voffset > 0 { if voffset + 4 > object_inline_num_bytes { @@ -278,26 +209,51 @@ pub mod p2p { let buf = tab.buf; let buf_len = buf.len(); - if tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { + if tab.loc > MAX_OFFSET_LOC || tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { return Err(Error::OutOfBounds); } let vtab_loc = { - let soffset_slice = &buf[tab.loc..tab.loc + flatbuffers::SIZE_SOFFSET]; + let soffset_slice = &buf[tab.loc..]; let soffset = flatbuffers::read_scalar::(soffset_slice); - (tab.loc as flatbuffers::SOffsetT - soffset) as usize - }; - if vtab_loc + flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET > buf_len { + if soffset >= 0 { + tab.loc.checked_sub(soffset as usize) + } else { + soffset + .checked_neg() + .and_then(|foffset| tab.loc.checked_add(foffset as usize)) + } + } + .ok_or(Error::OutOfBounds)?; + if vtab_loc + .checked_add(flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } let vtab = tab.vtable(); let vtab_num_bytes = vtab.num_bytes(); - if vtab_loc + vtab_num_bytes > buf_len { + let object_inline_num_bytes = vtab.object_inline_num_bytes(); + if vtab_num_bytes < flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET + || object_inline_num_bytes < flatbuffers::SIZE_SOFFSET + { return Err(Error::OutOfBounds); } - let object_inline_num_bytes = vtab.object_inline_num_bytes(); - if tab.loc + object_inline_num_bytes > buf_len { + if vtab_loc + .checked_add(vtab_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { + return Err(Error::OutOfBounds); + } + if tab + .loc + .checked_add(object_inline_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } @@ -310,16 +266,21 @@ pub mod p2p { } } - if Self::VT_ADDRS as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_ADDRS as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_ADDRS) as usize; if voffset > 0 { if voffset + 4 > object_inline_num_bytes { return Err(Error::OutOfBounds); } - let addrs_verifier = - VectorVerifier::follow(buf, try_follow_uoffset(buf, tab.loc + voffset)?); - addrs_verifier.verify_reference_elements::()?; + let addrs_verifier = VectorVerifier::follow( + buf, + try_follow_uoffset(buf, tab.loc + voffset)?, + ); + addrs_verifier + .verify_reference_elements::()?; } } @@ -333,26 +294,51 @@ pub mod p2p { let buf = tab.buf; let buf_len = buf.len(); - if tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { + if tab.loc > MAX_OFFSET_LOC || tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { return Err(Error::OutOfBounds); } let vtab_loc = { - let soffset_slice = &buf[tab.loc..tab.loc + flatbuffers::SIZE_SOFFSET]; + let soffset_slice = &buf[tab.loc..]; let soffset = flatbuffers::read_scalar::(soffset_slice); - (tab.loc as flatbuffers::SOffsetT - soffset) as usize - }; - if vtab_loc + flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET > buf_len { + if soffset >= 0 { + tab.loc.checked_sub(soffset as usize) + } else { + soffset + .checked_neg() + .and_then(|foffset| tab.loc.checked_add(foffset as usize)) + } + } + .ok_or(Error::OutOfBounds)?; + if vtab_loc + .checked_add(flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } let vtab = tab.vtable(); let vtab_num_bytes = vtab.num_bytes(); - if vtab_loc + vtab_num_bytes > buf_len { + let object_inline_num_bytes = vtab.object_inline_num_bytes(); + if vtab_num_bytes < flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET + || object_inline_num_bytes < flatbuffers::SIZE_SOFFSET + { return Err(Error::OutOfBounds); } - let object_inline_num_bytes = vtab.object_inline_num_bytes(); - if tab.loc + object_inline_num_bytes > buf_len { + if vtab_loc + .checked_add(vtab_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { + return Err(Error::OutOfBounds); + } + if tab + .loc + .checked_add(object_inline_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } @@ -365,7 +351,9 @@ pub mod p2p { } } - if Self::VT_ADDR as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_ADDR as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_ADDR) as usize; if voffset > 0 { if voffset + 4 > object_inline_num_bytes { diff --git a/protocols/ping/Cargo.toml b/protocols/ping/Cargo.toml index b05d51d0..a90d1993 100644 --- a/protocols/ping/Cargo.toml +++ b/protocols/ping/Cargo.toml @@ -13,6 +13,7 @@ edition = "2018" p2p = { path = "../..", version = "0.2.0-alpha.1", package = "tentacle" } log = "0.4" flatbuffers = "0.5.0" +flatbuffers-verifier = "0.1.8" fnv = "1.0.6" generic-channel = "0.2.0" bytes = "0.4" diff --git a/protocols/ping/src/lib.rs b/protocols/ping/src/lib.rs index 643e704b..6c9926b0 100644 --- a/protocols/ping/src/lib.rs +++ b/protocols/ping/src/lib.rs @@ -6,8 +6,9 @@ mod protocol_generated; #[allow(dead_code)] mod protocol_generated_verifier; -use crate::{protocol_generated::p2p::ping::*, protocol_generated_verifier::get_root}; +use crate::protocol_generated::p2p::ping::*; use flatbuffers::{FlatBufferBuilder, WIPOffset}; +use flatbuffers_verifier::get_root; use fnv::FnvHashMap; use generic_channel::Sender; use log::{debug, error}; diff --git a/protocols/ping/src/protocol_generated_verifier.rs b/protocols/ping/src/protocol_generated_verifier.rs index 5f56fc56..d888faa4 100644 --- a/protocols/ping/src/protocol_generated_verifier.rs +++ b/protocols/ping/src/protocol_generated_verifier.rs @@ -1,151 +1,24 @@ //! This file is auto-generated by cfbc. use super::protocol_generated as reader; -use flatbuffers; -use std::error; -use std::fmt; -use std::result; - -#[derive(Debug, Eq, PartialEq)] -pub enum Error { - OutOfBounds, - NonNullTerminatedString, - UnmatchedUnion, -} - -pub type Result = result::Result<(), Error>; - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::OutOfBounds => write!(f, "memory access is out of bounds"), - Error::NonNullTerminatedString => write!(f, "string is not terminated with null"), - Error::UnmatchedUnion => write!(f, "union type and value does not match"), - } - } -} - -impl error::Error for Error {} - -pub trait Verify { - fn verify(&self) -> Result; -} - -fn read_uoffset(buf: &[u8], offset_loc: usize) -> usize { - flatbuffers::read_scalar::(&buf[offset_loc..]) as usize -} - -fn try_read_uoffset(buf: &[u8], offset_loc: usize) -> result::Result { - if offset_loc + flatbuffers::SIZE_UOFFSET <= buf.len() { - Ok(read_uoffset(buf, offset_loc)) - } else { - Err(Error::OutOfBounds) - } -} - -pub fn try_follow_uoffset(buf: &[u8], offset_loc: usize) -> result::Result { - try_read_uoffset(buf, offset_loc).map(|offset| offset_loc + offset) -} - -pub struct StringVerifier<'a> { - buf: &'a [u8], - loc: usize, -} - -impl<'a> flatbuffers::Follow<'a> for StringVerifier<'a> { - type Inner = Self; - fn follow(buf: &'a [u8], loc: usize) -> Self { - Self { buf, loc } - } -} - -impl<'a> Verify for StringVerifier<'a> { - fn verify(&self) -> Result { - let buf_len = self.buf.len(); - - let len = try_read_uoffset(self.buf, self.loc)?; - if self.loc + flatbuffers::SIZE_UOFFSET + len + 1 > buf_len { - return Err(Error::OutOfBounds); - } - - if self.buf[self.loc + flatbuffers::SIZE_UOFFSET + len] != 0 { - return Err(Error::NonNullTerminatedString); - } - - Ok(()) - } -} - -pub struct VectorVerifier<'a> { - buf: &'a [u8], - loc: usize, -} - -impl<'a> flatbuffers::Follow<'a> for VectorVerifier<'a> { - type Inner = Self; - fn follow(buf: &'a [u8], loc: usize) -> Self { - Self { buf, loc } - } -} - -impl<'a> VectorVerifier<'a> { - pub fn verify_scalar_elements(&self, scalar_size: usize) -> Result { - let len = try_read_uoffset(self.buf, self.loc)?; - - if self.loc + flatbuffers::SIZE_UOFFSET + len * scalar_size > self.buf.len() { - return Err(Error::OutOfBounds); - } - - Ok(()) - } - - pub fn verify_reference_elements(&self) -> Result - where - E: flatbuffers::Follow<'a>, - >::Inner: Verify, - { - let len = try_read_uoffset(self.buf, self.loc)?; - - let mut offset_loc = self.loc + flatbuffers::SIZE_UOFFSET; - let end_loc = offset_loc + len * flatbuffers::SIZE_UOFFSET; - if end_loc > self.buf.len() { - return Err(Error::OutOfBounds); - } - - while offset_loc < end_loc { - E::follow(self.buf, offset_loc + read_uoffset(self.buf, offset_loc)).verify()?; - offset_loc += flatbuffers::SIZE_UOFFSET; - } - - Ok(()) - } -} - -pub fn get_root<'a, T>(data: &'a [u8]) -> result::Result -where - T: flatbuffers::Follow<'a> + 'a, - T::Inner: Verify, -{ - if data.len() < flatbuffers::SIZE_UOFFSET { - return Err(Error::OutOfBounds); - } - - let root = flatbuffers::get_root::(data); - root.verify()?; - Ok(root) -} pub mod p2p { #![allow(unused_imports)] use super::reader::p2p as reader; - pub use super::{try_follow_uoffset, Error, Result, StringVerifier, VectorVerifier, Verify}; use flatbuffers::{self, Follow}; + use flatbuffers_verifier::{ + try_follow_uoffset, Error, Result, StringVerifier, VectorVerifier, Verify, + MAX_OFFSET_LOC, + }; pub mod ping { #![allow(unused_imports)] use super::reader::ping as reader; - pub use super::{try_follow_uoffset, Error, Result, StringVerifier, VectorVerifier, Verify}; use flatbuffers::{self, Follow}; + use flatbuffers_verifier::{ + try_follow_uoffset, Error, Result, StringVerifier, VectorVerifier, Verify, + MAX_OFFSET_LOC, + }; impl<'a> Verify for reader::Ping<'a> { fn verify(&self) -> Result { @@ -153,26 +26,51 @@ pub mod p2p { let buf = tab.buf; let buf_len = buf.len(); - if tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { + if tab.loc > MAX_OFFSET_LOC || tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { return Err(Error::OutOfBounds); } let vtab_loc = { - let soffset_slice = &buf[tab.loc..tab.loc + flatbuffers::SIZE_SOFFSET]; + let soffset_slice = &buf[tab.loc..]; let soffset = flatbuffers::read_scalar::(soffset_slice); - (tab.loc as flatbuffers::SOffsetT - soffset) as usize - }; - if vtab_loc + flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET > buf_len { + if soffset >= 0 { + tab.loc.checked_sub(soffset as usize) + } else { + soffset + .checked_neg() + .and_then(|foffset| tab.loc.checked_add(foffset as usize)) + } + } + .ok_or(Error::OutOfBounds)?; + if vtab_loc + .checked_add(flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } let vtab = tab.vtable(); let vtab_num_bytes = vtab.num_bytes(); - if vtab_loc + vtab_num_bytes > buf_len { + let object_inline_num_bytes = vtab.object_inline_num_bytes(); + if vtab_num_bytes < flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET + || object_inline_num_bytes < flatbuffers::SIZE_SOFFSET + { return Err(Error::OutOfBounds); } - let object_inline_num_bytes = vtab.object_inline_num_bytes(); - if tab.loc + object_inline_num_bytes > buf_len { + if vtab_loc + .checked_add(vtab_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { + return Err(Error::OutOfBounds); + } + if tab + .loc + .checked_add(object_inline_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } @@ -185,9 +83,11 @@ pub mod p2p { } } - if Self::VT_NONCE as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_NONCE as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_NONCE) as usize; - if voffset > 0 && voffset + 4 > object_inline_num_bytes { + if voffset > 0 && object_inline_num_bytes - voffset < 4 { return Err(Error::OutOfBounds); } } @@ -202,26 +102,51 @@ pub mod p2p { let buf = tab.buf; let buf_len = buf.len(); - if tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { + if tab.loc > MAX_OFFSET_LOC || tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { return Err(Error::OutOfBounds); } let vtab_loc = { - let soffset_slice = &buf[tab.loc..tab.loc + flatbuffers::SIZE_SOFFSET]; + let soffset_slice = &buf[tab.loc..]; let soffset = flatbuffers::read_scalar::(soffset_slice); - (tab.loc as flatbuffers::SOffsetT - soffset) as usize - }; - if vtab_loc + flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET > buf_len { + if soffset >= 0 { + tab.loc.checked_sub(soffset as usize) + } else { + soffset + .checked_neg() + .and_then(|foffset| tab.loc.checked_add(foffset as usize)) + } + } + .ok_or(Error::OutOfBounds)?; + if vtab_loc + .checked_add(flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } let vtab = tab.vtable(); let vtab_num_bytes = vtab.num_bytes(); - if vtab_loc + vtab_num_bytes > buf_len { + let object_inline_num_bytes = vtab.object_inline_num_bytes(); + if vtab_num_bytes < flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET + || object_inline_num_bytes < flatbuffers::SIZE_SOFFSET + { return Err(Error::OutOfBounds); } - let object_inline_num_bytes = vtab.object_inline_num_bytes(); - if tab.loc + object_inline_num_bytes > buf_len { + if vtab_loc + .checked_add(vtab_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { + return Err(Error::OutOfBounds); + } + if tab + .loc + .checked_add(object_inline_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } @@ -234,14 +159,18 @@ pub mod p2p { } } - if Self::VT_PAYLOAD_TYPE as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_PAYLOAD_TYPE as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_PAYLOAD_TYPE) as usize; - if voffset > 0 && voffset + 1 > object_inline_num_bytes { + if voffset > 0 && object_inline_num_bytes - voffset < 1 { return Err(Error::OutOfBounds); } } - if Self::VT_PAYLOAD as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_PAYLOAD as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_PAYLOAD) as usize; if voffset > 0 { if voffset + 4 > object_inline_num_bytes { @@ -272,26 +201,51 @@ pub mod p2p { let buf = tab.buf; let buf_len = buf.len(); - if tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { + if tab.loc > MAX_OFFSET_LOC || tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { return Err(Error::OutOfBounds); } let vtab_loc = { - let soffset_slice = &buf[tab.loc..tab.loc + flatbuffers::SIZE_SOFFSET]; + let soffset_slice = &buf[tab.loc..]; let soffset = flatbuffers::read_scalar::(soffset_slice); - (tab.loc as flatbuffers::SOffsetT - soffset) as usize - }; - if vtab_loc + flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET > buf_len { + if soffset >= 0 { + tab.loc.checked_sub(soffset as usize) + } else { + soffset + .checked_neg() + .and_then(|foffset| tab.loc.checked_add(foffset as usize)) + } + } + .ok_or(Error::OutOfBounds)?; + if vtab_loc + .checked_add(flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } let vtab = tab.vtable(); let vtab_num_bytes = vtab.num_bytes(); - if vtab_loc + vtab_num_bytes > buf_len { + let object_inline_num_bytes = vtab.object_inline_num_bytes(); + if vtab_num_bytes < flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET + || object_inline_num_bytes < flatbuffers::SIZE_SOFFSET + { return Err(Error::OutOfBounds); } - let object_inline_num_bytes = vtab.object_inline_num_bytes(); - if tab.loc + object_inline_num_bytes > buf_len { + if vtab_loc + .checked_add(vtab_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { + return Err(Error::OutOfBounds); + } + if tab + .loc + .checked_add(object_inline_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } @@ -304,9 +258,11 @@ pub mod p2p { } } - if Self::VT_NONCE as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_NONCE as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_NONCE) as usize; - if voffset > 0 && voffset + 4 > object_inline_num_bytes { + if voffset > 0 && object_inline_num_bytes - voffset < 4 { return Err(Error::OutOfBounds); } } diff --git a/secio/Cargo.toml b/secio/Cargo.toml index b6e91df1..e84450ae 100644 --- a/secio/Cargo.toml +++ b/secio/Cargo.toml @@ -16,6 +16,7 @@ tokio = "0.1" log = "0.4.1" flatbuffers = "0.5.0" +flatbuffers-verifier = "0.1.8" secp256k1 = "0.12" hmac = "0.7.0" diff --git a/secio/src/handshake/handshake_generated_verifier.rs b/secio/src/handshake/handshake_generated_verifier.rs index d212dc98..8dc3b072 100644 --- a/secio/src/handshake/handshake_generated_verifier.rs +++ b/secio/src/handshake/handshake_generated_verifier.rs @@ -1,151 +1,24 @@ //! This file is auto-generated by cfbc. use super::handshake_generated as reader; -use flatbuffers; -use std::error; -use std::fmt; -use std::result; - -#[derive(Debug, Eq, PartialEq)] -pub enum Error { - OutOfBounds, - NonNullTerminatedString, - UnmatchedUnion, -} - -pub type Result = result::Result<(), Error>; - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::OutOfBounds => write!(f, "memory access is out of bounds"), - Error::NonNullTerminatedString => write!(f, "string is not terminated with null"), - Error::UnmatchedUnion => write!(f, "union type and value does not match"), - } - } -} - -impl error::Error for Error {} - -pub trait Verify { - fn verify(&self) -> Result; -} - -fn read_uoffset(buf: &[u8], offset_loc: usize) -> usize { - flatbuffers::read_scalar::(&buf[offset_loc..]) as usize -} - -fn try_read_uoffset(buf: &[u8], offset_loc: usize) -> result::Result { - if offset_loc + flatbuffers::SIZE_UOFFSET <= buf.len() { - Ok(read_uoffset(buf, offset_loc)) - } else { - Err(Error::OutOfBounds) - } -} - -pub fn try_follow_uoffset(buf: &[u8], offset_loc: usize) -> result::Result { - try_read_uoffset(buf, offset_loc).map(|offset| offset_loc + offset) -} - -pub struct StringVerifier<'a> { - buf: &'a [u8], - loc: usize, -} - -impl<'a> flatbuffers::Follow<'a> for StringVerifier<'a> { - type Inner = Self; - fn follow(buf: &'a [u8], loc: usize) -> Self { - Self { buf, loc } - } -} - -impl<'a> Verify for StringVerifier<'a> { - fn verify(&self) -> Result { - let buf_len = self.buf.len(); - - let len = try_read_uoffset(self.buf, self.loc)?; - if self.loc + flatbuffers::SIZE_UOFFSET + len + 1 > buf_len { - return Err(Error::OutOfBounds); - } - - if self.buf[self.loc + flatbuffers::SIZE_UOFFSET + len] != 0 { - return Err(Error::NonNullTerminatedString); - } - - Ok(()) - } -} - -pub struct VectorVerifier<'a> { - buf: &'a [u8], - loc: usize, -} - -impl<'a> flatbuffers::Follow<'a> for VectorVerifier<'a> { - type Inner = Self; - fn follow(buf: &'a [u8], loc: usize) -> Self { - Self { buf, loc } - } -} - -impl<'a> VectorVerifier<'a> { - pub fn verify_scalar_elements(&self, scalar_size: usize) -> Result { - let len = try_read_uoffset(self.buf, self.loc)?; - - if self.loc + flatbuffers::SIZE_UOFFSET + len * scalar_size > self.buf.len() { - return Err(Error::OutOfBounds); - } - - Ok(()) - } - - pub fn verify_reference_elements(&self) -> Result - where - E: flatbuffers::Follow<'a>, - >::Inner: Verify, - { - let len = try_read_uoffset(self.buf, self.loc)?; - - let mut offset_loc = self.loc + flatbuffers::SIZE_UOFFSET; - let end_loc = offset_loc + len * flatbuffers::SIZE_UOFFSET; - if end_loc > self.buf.len() { - return Err(Error::OutOfBounds); - } - - while offset_loc < end_loc { - E::follow(self.buf, offset_loc + read_uoffset(self.buf, offset_loc)).verify()?; - offset_loc += flatbuffers::SIZE_UOFFSET; - } - - Ok(()) - } -} - -pub fn get_root<'a, T>(data: &'a [u8]) -> result::Result -where - T: flatbuffers::Follow<'a> + 'a, - T::Inner: Verify, -{ - if data.len() < flatbuffers::SIZE_UOFFSET { - return Err(Error::OutOfBounds); - } - - let root = flatbuffers::get_root::(data); - root.verify()?; - Ok(root) -} pub mod p2p { #![allow(unused_imports)] use super::reader::p2p as reader; - pub use super::{try_follow_uoffset, Error, Result, StringVerifier, VectorVerifier, Verify}; use flatbuffers::{self, Follow}; + use flatbuffers_verifier::{ + try_follow_uoffset, Error, Result, StringVerifier, VectorVerifier, Verify, + MAX_OFFSET_LOC, + }; pub mod handshake { #![allow(unused_imports)] use super::reader::handshake as reader; - pub use super::{try_follow_uoffset, Error, Result, StringVerifier, VectorVerifier, Verify}; use flatbuffers::{self, Follow}; + use flatbuffers_verifier::{ + try_follow_uoffset, Error, Result, StringVerifier, VectorVerifier, Verify, + MAX_OFFSET_LOC, + }; impl<'a> Verify for reader::Exchange<'a> { fn verify(&self) -> Result { @@ -153,26 +26,51 @@ pub mod p2p { let buf = tab.buf; let buf_len = buf.len(); - if tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { + if tab.loc > MAX_OFFSET_LOC || tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { return Err(Error::OutOfBounds); } let vtab_loc = { - let soffset_slice = &buf[tab.loc..tab.loc + flatbuffers::SIZE_SOFFSET]; + let soffset_slice = &buf[tab.loc..]; let soffset = flatbuffers::read_scalar::(soffset_slice); - (tab.loc as flatbuffers::SOffsetT - soffset) as usize - }; - if vtab_loc + flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET > buf_len { + if soffset >= 0 { + tab.loc.checked_sub(soffset as usize) + } else { + soffset + .checked_neg() + .and_then(|foffset| tab.loc.checked_add(foffset as usize)) + } + } + .ok_or(Error::OutOfBounds)?; + if vtab_loc + .checked_add(flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } let vtab = tab.vtable(); let vtab_num_bytes = vtab.num_bytes(); - if vtab_loc + vtab_num_bytes > buf_len { + let object_inline_num_bytes = vtab.object_inline_num_bytes(); + if vtab_num_bytes < flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET + || object_inline_num_bytes < flatbuffers::SIZE_SOFFSET + { return Err(Error::OutOfBounds); } - let object_inline_num_bytes = vtab.object_inline_num_bytes(); - if tab.loc + object_inline_num_bytes > buf_len { + if vtab_loc + .checked_add(vtab_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { + return Err(Error::OutOfBounds); + } + if tab + .loc + .checked_add(object_inline_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } @@ -185,28 +83,36 @@ pub mod p2p { } } - if Self::VT_EPUBKEY as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_EPUBKEY as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_EPUBKEY) as usize; if voffset > 0 { if voffset + 4 > object_inline_num_bytes { return Err(Error::OutOfBounds); } - let epubkey_verifier = - VectorVerifier::follow(buf, try_follow_uoffset(buf, tab.loc + voffset)?); + let epubkey_verifier = VectorVerifier::follow( + buf, + try_follow_uoffset(buf, tab.loc + voffset)?, + ); epubkey_verifier.verify_scalar_elements(1)?; } } - if Self::VT_SIGNATURE as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_SIGNATURE as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_SIGNATURE) as usize; if voffset > 0 { if voffset + 4 > object_inline_num_bytes { return Err(Error::OutOfBounds); } - let signature_verifier = - VectorVerifier::follow(buf, try_follow_uoffset(buf, tab.loc + voffset)?); + let signature_verifier = VectorVerifier::follow( + buf, + try_follow_uoffset(buf, tab.loc + voffset)?, + ); signature_verifier.verify_scalar_elements(1)?; } } @@ -221,26 +127,51 @@ pub mod p2p { let buf = tab.buf; let buf_len = buf.len(); - if tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { + if tab.loc > MAX_OFFSET_LOC || tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { return Err(Error::OutOfBounds); } let vtab_loc = { - let soffset_slice = &buf[tab.loc..tab.loc + flatbuffers::SIZE_SOFFSET]; + let soffset_slice = &buf[tab.loc..]; let soffset = flatbuffers::read_scalar::(soffset_slice); - (tab.loc as flatbuffers::SOffsetT - soffset) as usize - }; - if vtab_loc + flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET > buf_len { + if soffset >= 0 { + tab.loc.checked_sub(soffset as usize) + } else { + soffset + .checked_neg() + .and_then(|foffset| tab.loc.checked_add(foffset as usize)) + } + } + .ok_or(Error::OutOfBounds)?; + if vtab_loc + .checked_add(flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } let vtab = tab.vtable(); let vtab_num_bytes = vtab.num_bytes(); - if vtab_loc + vtab_num_bytes > buf_len { + let object_inline_num_bytes = vtab.object_inline_num_bytes(); + if vtab_num_bytes < flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET + || object_inline_num_bytes < flatbuffers::SIZE_SOFFSET + { return Err(Error::OutOfBounds); } - let object_inline_num_bytes = vtab.object_inline_num_bytes(); - if tab.loc + object_inline_num_bytes > buf_len { + if vtab_loc + .checked_add(vtab_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { + return Err(Error::OutOfBounds); + } + if tab + .loc + .checked_add(object_inline_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } @@ -253,33 +184,43 @@ pub mod p2p { } } - if Self::VT_RAND as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_RAND as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_RAND) as usize; if voffset > 0 { if voffset + 4 > object_inline_num_bytes { return Err(Error::OutOfBounds); } - let rand_verifier = - VectorVerifier::follow(buf, try_follow_uoffset(buf, tab.loc + voffset)?); + let rand_verifier = VectorVerifier::follow( + buf, + try_follow_uoffset(buf, tab.loc + voffset)?, + ); rand_verifier.verify_scalar_elements(1)?; } } - if Self::VT_PUBKEY as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_PUBKEY as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_PUBKEY) as usize; if voffset > 0 { if voffset + 4 > object_inline_num_bytes { return Err(Error::OutOfBounds); } - let pubkey_verifier = - VectorVerifier::follow(buf, try_follow_uoffset(buf, tab.loc + voffset)?); + let pubkey_verifier = VectorVerifier::follow( + buf, + try_follow_uoffset(buf, tab.loc + voffset)?, + ); pubkey_verifier.verify_scalar_elements(1)?; } } - if Self::VT_EXCHANGES as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_EXCHANGES as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_EXCHANGES) as usize; if voffset > 0 { if voffset + 4 > object_inline_num_bytes { @@ -290,7 +231,9 @@ pub mod p2p { } } - if Self::VT_CIPHERS as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_CIPHERS as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_CIPHERS) as usize; if voffset > 0 { if voffset + 4 > object_inline_num_bytes { @@ -301,7 +244,9 @@ pub mod p2p { } } - if Self::VT_HASHES as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_HASHES as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_HASHES) as usize; if voffset > 0 { if voffset + 4 > object_inline_num_bytes { @@ -322,26 +267,51 @@ pub mod p2p { let buf = tab.buf; let buf_len = buf.len(); - if tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { + if tab.loc > MAX_OFFSET_LOC || tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { return Err(Error::OutOfBounds); } let vtab_loc = { - let soffset_slice = &buf[tab.loc..tab.loc + flatbuffers::SIZE_SOFFSET]; + let soffset_slice = &buf[tab.loc..]; let soffset = flatbuffers::read_scalar::(soffset_slice); - (tab.loc as flatbuffers::SOffsetT - soffset) as usize - }; - if vtab_loc + flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET > buf_len { + if soffset >= 0 { + tab.loc.checked_sub(soffset as usize) + } else { + soffset + .checked_neg() + .and_then(|foffset| tab.loc.checked_add(foffset as usize)) + } + } + .ok_or(Error::OutOfBounds)?; + if vtab_loc + .checked_add(flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } let vtab = tab.vtable(); let vtab_num_bytes = vtab.num_bytes(); - if vtab_loc + vtab_num_bytes > buf_len { + let object_inline_num_bytes = vtab.object_inline_num_bytes(); + if vtab_num_bytes < flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET + || object_inline_num_bytes < flatbuffers::SIZE_SOFFSET + { return Err(Error::OutOfBounds); } - let object_inline_num_bytes = vtab.object_inline_num_bytes(); - if tab.loc + object_inline_num_bytes > buf_len { + if vtab_loc + .checked_add(vtab_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { + return Err(Error::OutOfBounds); + } + if tab + .loc + .checked_add(object_inline_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } @@ -354,22 +324,28 @@ pub mod p2p { } } - if Self::VT_KEY_TYPE as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_KEY_TYPE as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_KEY_TYPE) as usize; - if voffset > 0 && voffset + 1 > object_inline_num_bytes { + if voffset > 0 && object_inline_num_bytes - voffset < 1 { return Err(Error::OutOfBounds); } } - if Self::VT_PUBKEY as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_PUBKEY as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_PUBKEY) as usize; if voffset > 0 { if voffset + 4 > object_inline_num_bytes { return Err(Error::OutOfBounds); } - let pubkey_verifier = - VectorVerifier::follow(buf, try_follow_uoffset(buf, tab.loc + voffset)?); + let pubkey_verifier = VectorVerifier::follow( + buf, + try_follow_uoffset(buf, tab.loc + voffset)?, + ); pubkey_verifier.verify_scalar_elements(1)?; } } diff --git a/secio/src/handshake/handshake_struct.rs b/secio/src/handshake/handshake_struct.rs index 0f9fa88c..0007b15e 100644 --- a/secio/src/handshake/handshake_struct.rs +++ b/secio/src/handshake/handshake_struct.rs @@ -1,13 +1,11 @@ -use crate::handshake::{ - handshake_generated::p2p::handshake::{ - Exchange as FBSExchange, ExchangeBuilder, Propose as FBSPropose, ProposeBuilder, - PublicKey as FBSPublicKey, PublicKeyBuilder, Type, - }, - handshake_generated_verifier::get_root, +use crate::handshake::handshake_generated::p2p::handshake::{ + Exchange as FBSExchange, ExchangeBuilder, Propose as FBSPropose, ProposeBuilder, + PublicKey as FBSPublicKey, PublicKeyBuilder, Type, }; use crate::peer_id::PeerId; use flatbuffers::FlatBufferBuilder; +use flatbuffers_verifier::get_root; #[derive(Clone, Default, PartialEq, Ord, PartialOrd, Eq, Debug)] pub struct Propose { diff --git a/src/protocol_select/mod.rs b/src/protocol_select/mod.rs index 5db142c2..099f0ec6 100644 --- a/src/protocol_select/mod.rs +++ b/src/protocol_select/mod.rs @@ -1,12 +1,10 @@ -use crate::protocol_select::{ - protocol_select_generated::p2p::protocol_select::{ - ProtocolInfo as FBSProtocolInfo, ProtocolInfoBuilder, - }, - protocol_select_generated_verifier::get_root, +use crate::protocol_select::protocol_select_generated::p2p::protocol_select::{ + ProtocolInfo as FBSProtocolInfo, ProtocolInfoBuilder, }; use bytes::Bytes; use flatbuffers::FlatBufferBuilder; +use flatbuffers_verifier::get_root; use futures::{future, prelude::*}; use log::debug; use std::cmp::Ordering; diff --git a/src/protocol_select/protocol_select_generated_verifier.rs b/src/protocol_select/protocol_select_generated_verifier.rs index 9d75d1e9..beb04617 100644 --- a/src/protocol_select/protocol_select_generated_verifier.rs +++ b/src/protocol_select/protocol_select_generated_verifier.rs @@ -1,151 +1,24 @@ //! This file is auto-generated by cfbc. use super::protocol_select_generated as reader; -use flatbuffers; -use std::error; -use std::fmt; -use std::result; - -#[derive(Debug, Eq, PartialEq)] -pub enum Error { - OutOfBounds, - NonNullTerminatedString, - UnmatchedUnion, -} - -pub type Result = result::Result<(), Error>; - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::OutOfBounds => write!(f, "memory access is out of bounds"), - Error::NonNullTerminatedString => write!(f, "string is not terminated with null"), - Error::UnmatchedUnion => write!(f, "union type and value does not match"), - } - } -} - -impl error::Error for Error {} - -pub trait Verify { - fn verify(&self) -> Result; -} - -fn read_uoffset(buf: &[u8], offset_loc: usize) -> usize { - flatbuffers::read_scalar::(&buf[offset_loc..]) as usize -} - -fn try_read_uoffset(buf: &[u8], offset_loc: usize) -> result::Result { - if offset_loc + flatbuffers::SIZE_UOFFSET <= buf.len() { - Ok(read_uoffset(buf, offset_loc)) - } else { - Err(Error::OutOfBounds) - } -} - -pub fn try_follow_uoffset(buf: &[u8], offset_loc: usize) -> result::Result { - try_read_uoffset(buf, offset_loc).map(|offset| offset_loc + offset) -} - -pub struct StringVerifier<'a> { - buf: &'a [u8], - loc: usize, -} - -impl<'a> flatbuffers::Follow<'a> for StringVerifier<'a> { - type Inner = Self; - fn follow(buf: &'a [u8], loc: usize) -> Self { - Self { buf, loc } - } -} - -impl<'a> Verify for StringVerifier<'a> { - fn verify(&self) -> Result { - let buf_len = self.buf.len(); - - let len = try_read_uoffset(self.buf, self.loc)?; - if self.loc + flatbuffers::SIZE_UOFFSET + len + 1 > buf_len { - return Err(Error::OutOfBounds); - } - - if self.buf[self.loc + flatbuffers::SIZE_UOFFSET + len] != 0 { - return Err(Error::NonNullTerminatedString); - } - - Ok(()) - } -} - -pub struct VectorVerifier<'a> { - buf: &'a [u8], - loc: usize, -} - -impl<'a> flatbuffers::Follow<'a> for VectorVerifier<'a> { - type Inner = Self; - fn follow(buf: &'a [u8], loc: usize) -> Self { - Self { buf, loc } - } -} - -impl<'a> VectorVerifier<'a> { - pub fn verify_scalar_elements(&self, scalar_size: usize) -> Result { - let len = try_read_uoffset(self.buf, self.loc)?; - - if self.loc + flatbuffers::SIZE_UOFFSET + len * scalar_size > self.buf.len() { - return Err(Error::OutOfBounds); - } - - Ok(()) - } - - pub fn verify_reference_elements(&self) -> Result - where - E: flatbuffers::Follow<'a>, - >::Inner: Verify, - { - let len = try_read_uoffset(self.buf, self.loc)?; - - let mut offset_loc = self.loc + flatbuffers::SIZE_UOFFSET; - let end_loc = offset_loc + len * flatbuffers::SIZE_UOFFSET; - if end_loc > self.buf.len() { - return Err(Error::OutOfBounds); - } - - while offset_loc < end_loc { - E::follow(self.buf, offset_loc + read_uoffset(self.buf, offset_loc)).verify()?; - offset_loc += flatbuffers::SIZE_UOFFSET; - } - - Ok(()) - } -} - -pub fn get_root<'a, T>(data: &'a [u8]) -> result::Result -where - T: flatbuffers::Follow<'a> + 'a, - T::Inner: Verify, -{ - if data.len() < flatbuffers::SIZE_UOFFSET { - return Err(Error::OutOfBounds); - } - - let root = flatbuffers::get_root::(data); - root.verify()?; - Ok(root) -} pub mod p2p { #![allow(unused_imports)] use super::reader::p2p as reader; - pub use super::{try_follow_uoffset, Error, Result, StringVerifier, VectorVerifier, Verify}; use flatbuffers::{self, Follow}; + use flatbuffers_verifier::{ + try_follow_uoffset, Error, Result, StringVerifier, VectorVerifier, Verify, + MAX_OFFSET_LOC, + }; pub mod protocol_select { #![allow(unused_imports)] use super::reader::protocol_select as reader; - pub use super::{try_follow_uoffset, Error, Result, StringVerifier, VectorVerifier, Verify}; use flatbuffers::{self, Follow}; + use flatbuffers_verifier::{ + try_follow_uoffset, Error, Result, StringVerifier, VectorVerifier, Verify, + MAX_OFFSET_LOC, + }; impl<'a> Verify for reader::ProtocolInfo<'a> { fn verify(&self) -> Result { @@ -153,26 +26,51 @@ pub mod p2p { let buf = tab.buf; let buf_len = buf.len(); - if tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { + if tab.loc > MAX_OFFSET_LOC || tab.loc + flatbuffers::SIZE_SOFFSET > buf_len { return Err(Error::OutOfBounds); } let vtab_loc = { - let soffset_slice = &buf[tab.loc..tab.loc + flatbuffers::SIZE_SOFFSET]; + let soffset_slice = &buf[tab.loc..]; let soffset = flatbuffers::read_scalar::(soffset_slice); - (tab.loc as flatbuffers::SOffsetT - soffset) as usize - }; - if vtab_loc + flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET > buf_len { + if soffset >= 0 { + tab.loc.checked_sub(soffset as usize) + } else { + soffset + .checked_neg() + .and_then(|foffset| tab.loc.checked_add(foffset as usize)) + } + } + .ok_or(Error::OutOfBounds)?; + if vtab_loc + .checked_add(flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } let vtab = tab.vtable(); let vtab_num_bytes = vtab.num_bytes(); - if vtab_loc + vtab_num_bytes > buf_len { + let object_inline_num_bytes = vtab.object_inline_num_bytes(); + if vtab_num_bytes < flatbuffers::SIZE_VOFFSET + flatbuffers::SIZE_VOFFSET + || object_inline_num_bytes < flatbuffers::SIZE_SOFFSET + { return Err(Error::OutOfBounds); } - let object_inline_num_bytes = vtab.object_inline_num_bytes(); - if tab.loc + object_inline_num_bytes > buf_len { + if vtab_loc + .checked_add(vtab_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { + return Err(Error::OutOfBounds); + } + if tab + .loc + .checked_add(object_inline_num_bytes) + .filter(|loc| *loc <= buf_len) + .is_none() + { return Err(Error::OutOfBounds); } @@ -185,7 +83,9 @@ pub mod p2p { } } - if Self::VT_NAME as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_NAME as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_NAME) as usize; if voffset > 0 { if voffset + 4 > object_inline_num_bytes { @@ -196,15 +96,19 @@ pub mod p2p { } } - if Self::VT_SUPPORT_VERSIONS as usize + flatbuffers::SIZE_VOFFSET <= vtab_num_bytes { + if Self::VT_SUPPORT_VERSIONS as usize + flatbuffers::SIZE_VOFFSET + <= vtab_num_bytes + { let voffset = vtab.get(Self::VT_SUPPORT_VERSIONS) as usize; if voffset > 0 { if voffset + 4 > object_inline_num_bytes { return Err(Error::OutOfBounds); } - let support_versions_verifier = - VectorVerifier::follow(buf, try_follow_uoffset(buf, tab.loc + voffset)?); + let support_versions_verifier = VectorVerifier::follow( + buf, + try_follow_uoffset(buf, tab.loc + voffset)?, + ); support_versions_verifier.verify_reference_elements::()?; } } From 2e5d56ad975558732b34e1cffa814f3ad84eaf52 Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 1 Apr 2019 10:42:52 +0800 Subject: [PATCH 2/3] chore: cleanup gen_proto.sh Use `make gen-fb` to generate files, and `make clean-fb gen-fb` to regenerate all files. --- protocols/discovery/src/gen_proto.sh | 8 -------- protocols/identify/src/gen_proto.sh | 8 -------- protocols/ping/src/gen_proto.sh | 8 -------- secio/src/handshake/gen_proto.sh | 8 -------- src/protocol_select/gen_proto.sh | 8 -------- 5 files changed, 40 deletions(-) delete mode 100755 protocols/discovery/src/gen_proto.sh delete mode 100755 protocols/identify/src/gen_proto.sh delete mode 100755 protocols/ping/src/gen_proto.sh delete mode 100755 secio/src/handshake/gen_proto.sh delete mode 100755 src/protocol_select/gen_proto.sh diff --git a/protocols/discovery/src/gen_proto.sh b/protocols/discovery/src/gen_proto.sh deleted file mode 100755 index 26ef8cbd..00000000 --- a/protocols/discovery/src/gen_proto.sh +++ /dev/null @@ -1,8 +0,0 @@ -#! /bin/sh -# flatc version 1.10.0 -# pip install cfbc - -flatc --rust protocol.fbs -flatc -b --schema protocol.fbs -cfbc protocol.bfbs -rm *_builder.rs protocol.bfbs diff --git a/protocols/identify/src/gen_proto.sh b/protocols/identify/src/gen_proto.sh deleted file mode 100755 index 26ef8cbd..00000000 --- a/protocols/identify/src/gen_proto.sh +++ /dev/null @@ -1,8 +0,0 @@ -#! /bin/sh -# flatc version 1.10.0 -# pip install cfbc - -flatc --rust protocol.fbs -flatc -b --schema protocol.fbs -cfbc protocol.bfbs -rm *_builder.rs protocol.bfbs diff --git a/protocols/ping/src/gen_proto.sh b/protocols/ping/src/gen_proto.sh deleted file mode 100755 index 26ef8cbd..00000000 --- a/protocols/ping/src/gen_proto.sh +++ /dev/null @@ -1,8 +0,0 @@ -#! /bin/sh -# flatc version 1.10.0 -# pip install cfbc - -flatc --rust protocol.fbs -flatc -b --schema protocol.fbs -cfbc protocol.bfbs -rm *_builder.rs protocol.bfbs diff --git a/secio/src/handshake/gen_proto.sh b/secio/src/handshake/gen_proto.sh deleted file mode 100755 index 9749c60d..00000000 --- a/secio/src/handshake/gen_proto.sh +++ /dev/null @@ -1,8 +0,0 @@ -#! /bin/sh -# flatc version 1.10.0 -# pip install cfbc - -flatc --rust handshake.fbs -flatc -b --schema handshake.fbs -cfbc handshake.bfbs -rm *_builder.rs handshake.bfbs diff --git a/src/protocol_select/gen_proto.sh b/src/protocol_select/gen_proto.sh deleted file mode 100755 index 6a1d9fca..00000000 --- a/src/protocol_select/gen_proto.sh +++ /dev/null @@ -1,8 +0,0 @@ -#! /bin/sh -# flatc version 1.10.0 -# pip install cfbc - -flatc --rust protocol_select.fbs -flatc -b --schema protocol_select.fbs -cfbc protocol_select.bfbs -rm *_builder.rs protocol_select.bfbs From 271212683c22df364d818ec7cee8c3eaba3e9d99 Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 8 Apr 2019 22:10:11 +0800 Subject: [PATCH 3/3] chore: ensure cfbc version --- Makefile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index f20c2612..2c30deb7 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,10 @@ examples: ci: fmt clippy test examples git diff --exit-code Cargo.lock -%_generated_verifier.rs: %.fbs +check-cfbc-version: + test "$$($(CFBC) --version)" = 0.1.9 + +%_generated_verifier.rs: %.fbs check-cfbc-version $(FLATC) -b --schema -o $(shell dirname $@) $< $(CFBC) -o $(shell dirname $@) $*.bfbs rm -f $*_builder.rs $*.bfbs @@ -40,5 +43,4 @@ clean-fb: rm -f $(FLATC_RUST_FILES) $(FLATBUFFERS_VERIFIER_FILES) - -.PHONY: fmt clippy test examples ci gen-fb clean-fb +.PHONY: fmt clippy test examples ci gen-fb clean-fb check-cfbc-version