From c7735bbe8d9a93c7f733a6ca2104179244344d4d Mon Sep 17 00:00:00 2001 From: Bo Yao Date: Mon, 4 Nov 2019 11:57:51 -0800 Subject: [PATCH 1/4] fix zero size element vector deserialize security issue --- borsh-rs/borsh/src/de/mod.rs | 21 +++++++++++++++++---- borsh-rs/borsh/tests/test_zero_size.rs | 11 +++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 borsh-rs/borsh/tests/test_zero_size.rs diff --git a/borsh-rs/borsh/src/de/mod.rs b/borsh-rs/borsh/src/de/mod.rs index 9e20b1b98..30f38aee6 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; @@ -128,11 +128,24 @@ where 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 { + 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]) +} From 7a3b7de32dfb7423c8491f5676b9bfdf17c20105 Mon Sep 17 00:00:00 2001 From: Bo Yao Date: Mon, 4 Nov 2019 12:00:28 -0800 Subject: [PATCH 2/4] place to comment to correct place --- borsh-rs/borsh/src/de/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/borsh-rs/borsh/src/de/mod.rs b/borsh-rs/borsh/src/de/mod.rs index 30f38aee6..83ecc0173 100644 --- a/borsh-rs/borsh/src/de/mod.rs +++ b/borsh-rs/borsh/src/de/mod.rs @@ -127,7 +127,6 @@ where #[inline] fn deserialize(reader: &mut R) -> Result { let len = u32::deserialize(reader)?; - // TODO(16): return capacity allocation when we can safely do that. if size_of::() == 0 { let mut result = Vec::new(); result.push(T::deserialize(reader)?); @@ -140,6 +139,7 @@ where 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)?); From 85bffc6a164cb7dd8072d649ea56d30666499fb1 Mon Sep 17 00:00:00 2001 From: Bo Yao Date: Mon, 4 Nov 2019 17:38:44 -0800 Subject: [PATCH 3/4] update doc --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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. From a59d71a4d5ece91edcc31a760e5660430d6ea77d Mon Sep 17 00:00:00 2001 From: Bo Yao Date: Tue, 5 Nov 2019 10:53:17 -0800 Subject: [PATCH 4/4] update website --- docs/index.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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