Skip to content

Commit

Permalink
Adding tests for invalid inputs (#15)
Browse files Browse the repository at this point in the history
* Adding tests for invalid inputs

* Remove memory pre-allocation
  • Loading branch information
ilblackdragon authored Sep 13, 2019
1 parent a3b8ce4 commit 3a17c16
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 8 deletions.
23 changes: 15 additions & 8 deletions borsh-rs/borsh/src/de/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@ use std::collections::{BTreeMap, HashMap, HashSet};
use std::io::{Cursor, Error, Read};
use std::mem::size_of;

const ERROR_NOT_ALL_BYTES_READ: &str = "Not all bytes read";

/// A data-structure that can be de-serialized from binary format by NBOR.
pub trait BorshDeserialize: Sized {
fn deserialize<R: Read>(reader: &mut R) -> Result<Self, Error>;

/// Deserialize this instance from a slice of bytes.
fn try_from_slice(v: &[u8]) -> Result<Self, Error> {
let mut c = Cursor::new(v);
Self::deserialize(&mut c)
let result = Self::deserialize(&mut c)?;
if c.position() != v.len() as u64 {
return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, ERROR_NOT_ALL_BYTES_READ));
}
Ok(result)
}
}

Expand Down Expand Up @@ -56,10 +62,9 @@ macro_rules! impl_for_float {
let mut data = [0u8; size_of::<$type>()];
reader.read_exact(&mut data)?;
let res = $type::from_bits($int_type::from_le_bytes(data));
assert!(
!res.is_nan(),
"For portability reasons we do not allow to deserialize NaNs."
);
if res.is_nan() {
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "For portability reasons we do not allow to deserialize NaNs."))
}
Ok(res)
}
}
Expand Down Expand Up @@ -101,7 +106,7 @@ impl BorshDeserialize for String {
let mut result = vec![0; len as usize];
reader.read(&mut result)?;
String::from_utf8(result)
.map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err.to_string()))
.map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err.to_string()))
}
}

Expand All @@ -113,7 +118,8 @@ where
#[inline]
fn deserialize<R: Read>(reader: &mut R) -> Result<Self, Error> {
let len = u32::deserialize(reader)?;
let mut result = Vec::with_capacity(len as usize);
// TODO(16): return capacity allocation when we can safely do that.
let mut result = Vec::new();
for _ in 0..len {
result.push(T::deserialize(reader)?);
}
Expand Down Expand Up @@ -142,7 +148,8 @@ where
#[inline]
fn deserialize<R: Read>(reader: &mut R) -> Result<Self, Error> {
let len = u32::deserialize(reader)?;
let mut result = HashMap::with_capacity(len as usize);
// TODO(16): return capacity allocation when we can safely do that.
let mut result = HashMap::new();
for _ in 0..len {
let key = K::deserialize(reader)?;
let value = V::deserialize(reader)?;
Expand Down
61 changes: 61 additions & 0 deletions borsh-rs/borsh/tests/test_de_errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use borsh::BorshDeserialize;

#[derive(BorshDeserialize, Debug)]
enum A {
X,
Y
}

#[derive(BorshDeserialize, Debug)]
struct B {
x: u64,
y: u32,
}

#[test]
fn test_missing_bytes() {
let bytes = vec![1, 0];
assert_eq!(B::try_from_slice(&bytes).unwrap_err().to_string(), "failed to fill whole buffer");
}

#[test]
fn test_invalid_enum_variant() {
let bytes = vec![123];
assert_eq!(A::try_from_slice(&bytes).unwrap_err().to_string(), "Unexpected variant index: 123");
}

#[test]
fn test_extra_bytes() {
let bytes = vec![1, 0, 0, 0, 32, 32];
assert_eq!(<Vec<u8>>::try_from_slice(&bytes).unwrap_err().to_string(), "Not all bytes read");
}

#[test]
fn test_invalid_bool() {
let bytes = vec![255];
assert_eq!(<bool>::try_from_slice(&bytes).unwrap(), false);
}

#[test]
fn test_invalid_option() {
let bytes = vec![255, 32];
assert_eq!(<Option<u8>>::try_from_slice(&bytes).unwrap(), Some(32));
}

#[test]
fn test_invalid_length() {
let bytes = vec![255u8; 4];
assert_eq!(<Vec<u64>>::try_from_slice(&bytes).unwrap_err().to_string(), "failed to fill whole buffer");
}

#[test]
fn test_non_utf_string() {
let bytes = vec![1, 0, 0, 0, 0xC0];
assert_eq!(String::try_from_slice(&bytes).unwrap_err().to_string(), "invalid utf-8 sequence of 1 bytes from index 0");
}

#[test]
fn test_nan_float() {
let bytes = vec![0, 0, 192, 127];
assert_eq!(f32::try_from_slice(&bytes).unwrap_err().to_string(), "For portability reasons we do not allow to deserialize NaNs.");
}

0 comments on commit 3a17c16

Please sign in to comment.