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