Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Limit snappy input stream #1738

Closed
wants to merge 10 commits into from
63 changes: 61 additions & 2 deletions beacon_node/eth2_libp2p/src/rpc/codec/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,16 +177,22 @@ where
mod tests {
use super::super::ssz_snappy::*;
use super::*;
use crate::rpc::methods::StatusMessage;
use crate::rpc::protocol::*;
use snap::write::FrameEncoder;
use ssz::Encode;
use std::io::Write;
use types::{Epoch, Hash256, Slot};
use unsigned_varint::codec::Uvi;

type Spec = types::MainnetEthSpec;

#[test]
fn test_decode_status_message() {
let message = hex::decode("ff060000734e615070590032000006e71e7b54989925efd6c9cbcb8ceb9b5f71216f5137282bf6a1e3b50f64e42d6c7fb347abe07eb0db8200000005029e2800").unwrap();
let mut buf = BytesMut::new();
buf.extend_from_slice(&message);

type Spec = types::MainnetEthSpec;

let snappy_protocol_id =
ProtocolId::new(Protocol::Status, Version::V1, Encoding::SSZSnappy);

Expand All @@ -207,4 +213,57 @@ mod tests {
let _ = dbg!(snappy_decoded_message);
let _ = dbg!(snappy_decoded_chunk);
}

#[test]
fn test_decode_malicious_status_message() {
// Snappy stream identifier
let stream_identifier: &'static [u8] = b"\xFF\x06\x00\x00sNaPpY";

// byte 0(0xFE) is padding chunk type identifier for snappy messages
// byte 1,2,3 are chunk length (little endian)
let malicious_padding: &'static [u8] = b"\xFE\x00\x00\x00";

// Status message is 84 bytes uncompressed. `max_compressed_len` is 130.
let status_message_bytes = StatusMessage {
fork_digest: [0; 4],
finalized_root: Hash256::from_low_u64_be(0),
finalized_epoch: Epoch::new(1),
head_root: Hash256::from_low_u64_be(0),
head_slot: Slot::new(1),
}
.as_ssz_bytes();

let mut uvi_codec: Uvi<usize> = Uvi::default();
let mut dst = BytesMut::with_capacity(1024);

// Insert length-prefix
uvi_codec
.encode(status_message_bytes.len(), &mut dst)
.unwrap();

// Insert snappy stream identifier
dst.extend_from_slice(stream_identifier);

// Insert malicious padding of 80 bytes.
for _ in 0..20 {
dst.extend_from_slice(malicious_padding);
}

// Insert payload (42 bytes compressed)
let mut writer = FrameEncoder::new(Vec::new());
writer.write_all(&status_message_bytes).unwrap();
writer.flush().unwrap();
dst.extend_from_slice(writer.get_ref());

// 42 + 80 = 132 > max_compressed_len. Hence, decoding should fail with `InvalidData`.

let snappy_protocol_id =
ProtocolId::new(Protocol::Status, Version::V1, Encoding::SSZSnappy);

let mut snappy_outbound_codec =
SSZSnappyOutboundCodec::<Spec>::new(snappy_protocol_id, 1_048_576);

let snappy_decoded_message = snappy_outbound_codec.decode(&mut dst.clone()).unwrap_err();
assert_eq!(snappy_decoded_message, RPCError::InvalidData);
}
}
Loading