Stop untrusted preallocation during JoinSplit deserialization

Zebra believes the untrusted `JoinSplit` list size field when
deserializing `JoinSplit`s, and preallocates a `Vec` based on that size.

This is trivial a memory exhaustion attack.

Instead, use the current auto-growing implementation, which is limited
by the size of the message data.
This commit is contained in:
teor 2021-03-19 12:23:16 +10:00 committed by Deirdre Connolly
parent f19f0d0949
commit db2f920d96
1 changed files with 16 additions and 18 deletions

View File

@ -44,26 +44,24 @@ impl<P: ZkSnarkProof> ZcashSerialize for JoinSplitData<P> {
impl<P: ZkSnarkProof> ZcashDeserialize for Option<JoinSplitData<P>> { impl<P: ZkSnarkProof> ZcashDeserialize for Option<JoinSplitData<P>> {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> { fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
let num_joinsplits = reader.read_compactsize()?; let joinsplits: Vec<sprout::JoinSplit<P>> = Vec::zcash_deserialize(&mut reader)?;
match num_joinsplits {
0 => Ok(None), if joinsplits.is_empty() {
n => { Ok(None)
let first = sprout::JoinSplit::zcash_deserialize(&mut reader)?; } else {
let mut rest = Vec::with_capacity((n - 1) as usize); let (first, rest) = joinsplits
for _ in 0..(n - 1) { .split_first()
rest.push(sprout::JoinSplit::zcash_deserialize(&mut reader)?); .expect("a non-empty Vec must have at least one entry");
}
let pub_key = reader.read_32_bytes()?.into(); let pub_key = reader.read_32_bytes()?.into();
let sig = reader.read_64_bytes()?.into(); let sig = reader.read_64_bytes()?.into();
Ok(Some(JoinSplitData { Ok(Some(JoinSplitData {
first, first: first.clone(),
rest, rest: rest.to_vec(),
pub_key, pub_key,
sig, sig,
})) }))
} }
} }
}
} }
impl ZcashSerialize for Transaction { impl ZcashSerialize for Transaction {