Reject trailing bytes (#198)

This commit is contained in:
Lena Hellström 2020-05-26 12:40:04 -07:00 committed by GitHub
parent 1bc63deb73
commit e80f61b9da
8 changed files with 121 additions and 6 deletions

View File

@ -49,6 +49,7 @@ macro_rules! config_map {
(Unlimited, Little) => {
let $opts = DefaultOptions::new()
.with_fixint_encoding()
.allow_trailing_bytes()
.with_no_limit()
.with_little_endian();
$call
@ -56,6 +57,7 @@ macro_rules! config_map {
(Unlimited, Big) => {
let $opts = DefaultOptions::new()
.with_fixint_encoding()
.allow_trailing_bytes()
.with_no_limit()
.with_big_endian();
$call
@ -63,6 +65,7 @@ macro_rules! config_map {
(Unlimited, Native) => {
let $opts = DefaultOptions::new()
.with_fixint_encoding()
.allow_trailing_bytes()
.with_no_limit()
.with_native_endian();
$call
@ -71,6 +74,7 @@ macro_rules! config_map {
(Limited(l), Little) => {
let $opts = DefaultOptions::new()
.with_fixint_encoding()
.allow_trailing_bytes()
.with_limit(l)
.with_little_endian();
$call
@ -78,6 +82,7 @@ macro_rules! config_map {
(Limited(l), Big) => {
let $opts = DefaultOptions::new()
.with_fixint_encoding()
.allow_trailing_bytes()
.with_limit(l)
.with_big_endian();
$call
@ -85,6 +90,7 @@ macro_rules! config_map {
(Limited(l), Native) => {
let $opts = DefaultOptions::new()
.with_fixint_encoding()
.allow_trailing_bytes()
.with_limit(l)
.with_native_endian();
$call

View File

@ -8,16 +8,19 @@ pub(crate) use self::endian::BincodeByteOrder;
pub(crate) use self::int::IntEncoding;
pub(crate) use self::internal::*;
pub(crate) use self::limit::SizeLimit;
pub(crate) use self::trailing::TrailingBytes;
pub use self::endian::{BigEndian, LittleEndian, NativeEndian};
pub use self::int::{FixintEncoding, VarintEncoding};
pub use self::legacy::*;
pub use self::limit::{Bounded, Infinite};
pub use self::trailing::{AllowTrailing, RejectTrailing};
mod endian;
mod int;
mod legacy;
mod limit;
mod trailing;
/// The default options for bincode serialization/deserialization.
///
@ -50,6 +53,7 @@ impl InternalOptions for DefaultOptions {
type Limit = Infinite;
type Endian = LittleEndian;
type IntEncoding = VarintEncoding;
type Trailing = RejectTrailing;
#[inline(always)]
fn limit(&mut self) -> &mut Infinite {
@ -111,6 +115,16 @@ pub trait Options: InternalOptions + Sized {
WithOtherIntEncoding::new(self)
}
/// Sets the deserializer to reject trailing bytes
fn reject_trailing_bytes(self) -> WithOtherTrailing<Self, RejectTrailing> {
WithOtherTrailing::new(self)
}
/// Sets the deserializer to allow trailing bytes
fn allow_trailing_bytes(self) -> WithOtherTrailing<Self, AllowTrailing> {
WithOtherTrailing::new(self)
}
/// Serializes a serializable object into a `Vec` of bytes using this configuration
#[inline(always)]
fn serialize<S: ?Sized + serde::Serialize>(self, t: &S) -> Result<Vec<u8>> {
@ -229,6 +243,12 @@ pub struct WithOtherIntEncoding<O: Options, I: IntEncoding> {
_length: PhantomData<I>,
}
/// A configuration struct with a user-specified trailing bytes behavior.
pub struct WithOtherTrailing<O: Options, T: TrailingBytes> {
options: O,
_trailing: PhantomData<T>,
}
impl<O: Options, L: SizeLimit> WithOtherLimit<O, L> {
#[inline(always)]
pub(crate) fn new(options: O, limit: L) -> WithOtherLimit<O, L> {
@ -259,11 +279,21 @@ impl<O: Options, I: IntEncoding> WithOtherIntEncoding<O, I> {
}
}
impl<O: Options, T: TrailingBytes> WithOtherTrailing<O, T> {
#[inline(always)]
pub(crate) fn new(options: O) -> WithOtherTrailing<O, T> {
WithOtherTrailing {
options,
_trailing: PhantomData,
}
}
}
impl<O: Options, E: BincodeByteOrder + 'static> InternalOptions for WithOtherEndian<O, E> {
type Limit = O::Limit;
type Endian = E;
type IntEncoding = O::IntEncoding;
type Trailing = O::Trailing;
#[inline(always)]
fn limit(&mut self) -> &mut O::Limit {
self.options.limit()
@ -274,7 +304,7 @@ impl<O: Options, L: SizeLimit + 'static> InternalOptions for WithOtherLimit<O, L
type Limit = L;
type Endian = O::Endian;
type IntEncoding = O::IntEncoding;
type Trailing = O::Trailing;
fn limit(&mut self) -> &mut L {
&mut self.new_limit
}
@ -284,6 +314,18 @@ impl<O: Options, I: IntEncoding + 'static> InternalOptions for WithOtherIntEncod
type Limit = O::Limit;
type Endian = O::Endian;
type IntEncoding = I;
type Trailing = O::Trailing;
fn limit(&mut self) -> &mut O::Limit {
self.options.limit()
}
}
impl<O: Options, T: TrailingBytes + 'static> InternalOptions for WithOtherTrailing<O, T> {
type Limit = O::Limit;
type Endian = O::Endian;
type IntEncoding = O::IntEncoding;
type Trailing = T;
fn limit(&mut self) -> &mut O::Limit {
self.options.limit()
@ -297,6 +339,7 @@ mod internal {
type Limit: SizeLimit + 'static;
type Endian: BincodeByteOrder + 'static;
type IntEncoding: IntEncoding + 'static;
type Trailing: TrailingBytes + 'static;
fn limit(&mut self) -> &mut Self::Limit;
}
@ -305,6 +348,7 @@ mod internal {
type Limit = O::Limit;
type Endian = O::Endian;
type IntEncoding = O::IntEncoding;
type Trailing = O::Trailing;
#[inline(always)]
fn limit(&mut self) -> &mut Self::Limit {

37
src/config/trailing.rs Normal file
View File

@ -0,0 +1,37 @@
use de::read::SliceReader;
use {ErrorKind, Result};
/// A trait for erroring deserialization if not all bytes were read.
pub trait TrailingBytes {
/// Checks a given slice reader to determine if deserialization used all bytes in the slice.
fn check_end(reader: &SliceReader) -> Result<()>;
}
/// A TrailingBytes config that will allow trailing bytes in slices after deserialization.
#[derive(Copy, Clone)]
pub struct AllowTrailing;
/// A TrailingBytes config that will cause bincode to produce an error if bytes are left over in the slice when deserialization is complete.
#[derive(Copy, Clone)]
pub struct RejectTrailing;
impl TrailingBytes for AllowTrailing {
#[inline(always)]
fn check_end(_reader: &SliceReader) -> Result<()> {
Ok(())
}
}
impl TrailingBytes for RejectTrailing {
#[inline(always)]
fn check_end(reader: &SliceReader) -> Result<()> {
if reader.is_finished() {
Ok(())
} else {
Err(Box::new(ErrorKind::Custom(
"Slice had bytes remaining after deserialization".to_string(),
)))
}
}
}

View File

@ -26,7 +26,7 @@ pub mod read;
/// let bytes_read = d.bytes_read();
/// ```
pub struct Deserializer<R, O: Options> {
reader: R,
pub(crate) reader: R,
options: O,
}

View File

@ -52,6 +52,10 @@ impl<'storage> SliceReader<'storage> {
self.slice = remaining;
Ok(read_slice)
}
pub(crate) fn is_finished(&self) -> bool {
self.slice.is_empty()
}
}
impl<R> IoReader<R> {

View File

@ -2,7 +2,7 @@ use serde;
use std::io::{Read, Write};
use std::marker::PhantomData;
use config::{Infinite, InternalOptions, Options, SizeLimit};
use config::{Infinite, InternalOptions, Options, SizeLimit, TrailingBytes};
use de::read::BincodeRead;
use Result;
@ -111,7 +111,14 @@ where
T: serde::de::DeserializeSeed<'a>,
O: InternalOptions,
{
let reader = ::de::read::SliceReader::new(bytes);
let options = ::config::WithOtherLimit::new(options, Infinite);
deserialize_from_custom_seed(seed, reader, options)
let reader = ::de::read::SliceReader::new(bytes);
let mut deserializer = ::de::Deserializer::with_bincode_read(reader, options);
let val = seed.deserialize(&mut deserializer)?;
match O::Trailing::check_end(&deserializer.reader) {
Ok(_) => Ok(val),
Err(err) => Err(err),
}
}

View File

@ -94,6 +94,7 @@ where
{
DefaultOptions::new()
.with_fixint_encoding()
.allow_trailing_bytes()
.serialize(value)
}
@ -107,6 +108,7 @@ where
{
DefaultOptions::new()
.with_fixint_encoding()
.allow_trailing_bytes()
.deserialize_from(reader)
}
@ -122,6 +124,7 @@ where
{
DefaultOptions::new()
.with_fixint_encoding()
.allow_trailing_bytes()
.deserialize_from_custom(reader)
}
@ -136,6 +139,7 @@ where
{
DefaultOptions::new()
.with_fixint_encoding()
.allow_trailing_bytes()
.deserialize_in_place(reader, place)
}
@ -146,6 +150,7 @@ where
{
DefaultOptions::new()
.with_fixint_encoding()
.allow_trailing_bytes()
.deserialize(bytes)
}
@ -156,5 +161,6 @@ where
{
DefaultOptions::new()
.with_fixint_encoding()
.allow_trailing_bytes()
.serialized_size(value)
}

View File

@ -277,6 +277,17 @@ fn deserializing_errors() {
}
}
#[test]
fn trailing_bytes() {
match DefaultOptions::new()
.deserialize::<char>(b"1x")
.map_err(|e| *e)
{
Err(ErrorKind::Custom(_)) => {}
other => panic!("Expecting TrailingBytes, got {:?}", other),
}
}
#[test]
fn too_big_deserialize() {
let serialized = vec![0, 0, 0, 3];