Skip to content

Commit f00fbb0

Browse files
authored
fix: handle big messages in P2P layer (#264)
This PR adds additional "big message" checks in the P2P layer: - when encoding a payload, check it doesn't go over the max uncompressed size - when responding to `BlocksByRoot` requests, skip big blocks None of these code paths should be triggered during normal operation, but they improve network stability by keeping errors local.
1 parent 172d9b0 commit f00fbb0

2 files changed

Lines changed: 23 additions & 4 deletions

File tree

crates/net/p2p/src/req_resp/codec.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ use std::io;
22

33
use libp2p::futures::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
44
use libssz::{SszDecode, SszEncode};
5-
use tracing::{debug, trace};
5+
use tracing::{debug, trace, warn};
66

77
use super::{
8-
encoding::{decode_payload, write_payload},
8+
encoding::{MAX_PAYLOAD_SIZE, decode_payload, write_payload},
99
messages::{
1010
BLOCKS_BY_ROOT_PROTOCOL_V1, ErrorMessage, Request, Response, ResponseCode, ResponsePayload,
1111
STATUS_PROTOCOL_V1, Status,
@@ -109,10 +109,21 @@ impl libp2p::request_response::Codec for Codec {
109109
write_payload(io, &encoded).await
110110
}
111111
ResponsePayload::BlocksByRoot(blocks) => {
112-
// Write each block as separate chunk
112+
// Write each block as a separate chunk.
113+
// Encode first, then check size before writing the SUCCESS
114+
// code byte. This avoids corrupting the stream if a block
115+
// exceeds MAX_PAYLOAD_SIZE (the SUCCESS byte would already
116+
// be on the wire with no payload following).
113117
for block in blocks {
114-
io.write_all(&[ResponseCode::SUCCESS.into()]).await?;
115118
let encoded = block.to_ssz();
119+
if encoded.len() > MAX_PAYLOAD_SIZE - 1024 {
120+
warn!(
121+
size = encoded.len(),
122+
"Skipping oversized block in BlocksByRoot response"
123+
);
124+
continue;
125+
}
126+
io.write_all(&[ResponseCode::SUCCESS.into()]).await?;
116127
write_payload(io, &encoded).await?;
117128
}
118129
// Empty response if no blocks found (stream just ends)

crates/net/p2p/src/req_resp/encoding.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ where
5252
T: AsyncWrite + Unpin,
5353
{
5454
let uncompressed_size = encoded.len();
55+
// Stop ourselves from sending messages our peers won't receive.
56+
// Leave some leeway for response codes and the varint encoding of the size.
57+
if uncompressed_size > MAX_PAYLOAD_SIZE - 1024 {
58+
return Err(io::Error::new(
59+
io::ErrorKind::InvalidData,
60+
"message size exceeds maximum allowed",
61+
));
62+
}
5563
let mut compressor = FrameEncoder::new(encoded);
5664

5765
let mut buf = Vec::new();

0 commit comments

Comments
 (0)