diff --git a/README.md b/README.md index 06828f3c7..68cc0beb2 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Why do we need yet another serialization format? Borsh is the first serializer t * Consistent means there is a bijective mapping between objects and their binary representations. There is no two binary representations that deserialize into the same object. This is extremely useful for applications that use binary representation to compute hash; * Borsh comes with a full specification that can be used for implementations in other languages; -* Safe. Borsh implementations use safe coding practices. In Rust, Borsh uses only safe code; +* Safe. Borsh implementations use safe coding practices. In Rust, Borsh uses almost only safe code, with one exception usage of `unsafe` to avoid an exhaustion attack; * Speed. In Rust, Borsh achieves high performance by opting out from [Serde](https://serde.rs) which makes it faster than [bincode](https://github.com/servo/bincode) in some cases; which also reduces the code size. diff --git a/borsh-rs/borsh/src/de/mod.rs b/borsh-rs/borsh/src/de/mod.rs index 9e20b1b98..83ecc0173 100644 --- a/borsh-rs/borsh/src/de/mod.rs +++ b/borsh-rs/borsh/src/de/mod.rs @@ -1,6 +1,6 @@ use std::collections::{BTreeMap, HashMap, HashSet}; use std::io::{Cursor, Error, Read}; -use std::mem::size_of; +use std::mem::{forget, size_of}; mod hint; @@ -127,12 +127,25 @@ where #[inline] fn deserialize(reader: &mut R) -> Result { let len = u32::deserialize(reader)?; - // TODO(16): return capacity allocation when we can safely do that. - let mut result = Vec::with_capacity(hint::cautious::(len)); - for _ in 0..len { + if size_of::() == 0 { + let mut result = Vec::new(); result.push(T::deserialize(reader)?); + + let p = result.as_mut_ptr(); + unsafe { + forget(result); + let len = len as usize; + let result = Vec::from_raw_parts(p, len, len); + Ok(result) + } + } else { + // TODO(16): return capacity allocation when we can safely do that. + let mut result = Vec::with_capacity(hint::cautious::(len)); + for _ in 0..len { + result.push(T::deserialize(reader)?); + } + Ok(result) } - Ok(result) } } diff --git a/borsh-rs/borsh/tests/test_zero_size.rs b/borsh-rs/borsh/tests/test_zero_size.rs new file mode 100644 index 000000000..7d26f558e --- /dev/null +++ b/borsh-rs/borsh/tests/test_zero_size.rs @@ -0,0 +1,11 @@ +use borsh::{BorshDeserialize, BorshSerialize}; + +#[derive(BorshDeserialize, PartialEq, Debug)] +struct A; + +#[test] +fn test_deserialize_vector_to_many_zero_size_struct() { + let v = [0u8, 0u8, 0u8, 64u8]; + let a = Vec::::try_from_slice(&v).unwrap(); + assert_eq!(A {}, a[usize::pow(2, 30) - 1]) +} diff --git a/docs/index.html b/docs/index.html index 8d4ffbd16..00e455c10 100644 --- a/docs/index.html +++ b/docs/index.html @@ -104,7 +104,8 @@

Borsh, binary serializer for security-critical projects.

Safety
- Borsh implementations use safe coding practices. In Rust, Borsh uses only safe code; + Borsh implementations use safe coding practices. In Rust, Borsh uses almost only safe code, + with one exception to avoid exhaustion attack;
Specification