feat(proto): enforce maximum length for server fill parameter

Move MAX_SERVER_FILL_BYTES to irtt-proto and validate it during decoding to prevent processing oversized parameters.
This commit is contained in:
Nils Andreas Svee committed 2026-05-17 22:59:57 +07:00
1 parent 2d6581f31f
commit 64da60d480
3 files changed
+41 -2

No files matched your search

+1 -1
View File
@@ -1,12 +1,12 @@
use std::{net::SocketAddr, time::Duration}; use std::{net::SocketAddr, time::Duration};
pub use irtt_proto::MAX_SERVER_FILL_BYTES;
use irtt_proto::{Clock, ReceivedStats, StampAt}; use irtt_proto::{Clock, ReceivedStats, StampAt};
pub(crate) const DEFAULT_PORT: u16 = 2112; pub(crate) const DEFAULT_PORT: u16 = 2112;
pub const MAX_DSCP_CODEPOINT: u8 = 63; pub const MAX_DSCP_CODEPOINT: u8 = 63;
pub const MAX_TTL: u32 = 255; pub const MAX_TTL: u32 = 255;
pub const MAX_UDP_PAYLOAD_LENGTH: u32 = 65_507; pub const MAX_UDP_PAYLOAD_LENGTH: u32 = 65_507;
pub const MAX_SERVER_FILL_BYTES: usize = 32;
pub(crate) const DEFAULT_DURATION: Duration = Duration::from_secs(3); pub(crate) const DEFAULT_DURATION: Duration = Duration::from_secs(3);
pub(crate) const DEFAULT_INTERVAL: Duration = Duration::from_secs(1); pub(crate) const DEFAULT_INTERVAL: Duration = Duration::from_secs(1);
pub(crate) const DEFAULT_OPEN_TIMEOUTS: [Duration; 4] = [ pub(crate) const DEFAULT_OPEN_TIMEOUTS: [Duration; 4] = [
+1 -1
View File
@@ -17,7 +17,7 @@ pub use flags::*;
pub use hmac::{compute_hmac, compute_hmac_in_place, verify_hmac}; pub use hmac::{compute_hmac, compute_hmac_in_place, verify_hmac};
pub use layout::{echo_header_len, echo_packet_len, PacketLayout}; pub use layout::{echo_header_len, echo_packet_len, PacketLayout};
pub use open::{decode_open_reply, encode_open_request, OpenReply, OpenRequest}; pub use open::{decode_open_reply, encode_open_request, OpenReply, OpenRequest};
pub use params::{Clock, Params, ReceivedStats, ServerFill, StampAt}; pub use params::{Clock, Params, ReceivedStats, ServerFill, StampAt, MAX_SERVER_FILL_BYTES};
pub const MAGIC: [u8; 3] = [0x14, 0xA7, 0x5B]; pub const MAGIC: [u8; 3] = [0x14, 0xA7, 0x5B];
pub const PROTOCOL_VERSION: i64 = 1; pub const PROTOCOL_VERSION: i64 = 1;
+39
View File
@@ -1,5 +1,7 @@
use crate::{varint, ProtoError, Result, PROTOCOL_VERSION}; use crate::{varint, ProtoError, Result, PROTOCOL_VERSION};
pub const MAX_SERVER_FILL_BYTES: usize = 32;
#[derive(Debug, Clone, PartialEq, Eq, Default)] #[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Params { pub struct Params {
pub protocol_version: i64, pub protocol_version: i64,
@@ -59,6 +61,12 @@ impl Params {
pos += used; pos += used;
let len = usize::try_from(len) let len = usize::try_from(len)
.map_err(|_| ProtoError::ParameterLengthTooLarge { tag, length: len })?; .map_err(|_| ProtoError::ParameterLengthTooLarge { tag, length: len })?;
if len > MAX_SERVER_FILL_BYTES {
return Err(ProtoError::ParameterLengthTooLarge {
tag,
length: len as u64,
});
}
if input.len().saturating_sub(pos) < len { if input.len().saturating_sub(pos) < len {
return Err(ProtoError::MalformedParams); return Err(ProtoError::MalformedParams);
} }
@@ -206,6 +214,14 @@ mod tests {
assert_eq!(Params::decode(&params.encode()), Ok(params)); assert_eq!(Params::decode(&params.encode()), Ok(params));
} }
fn encode_server_fill_value(value: &[u8]) -> Vec<u8> {
let mut encoded = Vec::new();
varint::encode_uvarint(9, &mut encoded);
varint::encode_uvarint(value.len() as u64, &mut encoded);
encoded.extend_from_slice(value);
encoded
}
#[test] #[test]
fn params_round_trip() { fn params_round_trip() {
let params = Params { let params = Params {
@@ -323,6 +339,29 @@ mod tests {
}); });
} }
#[test]
fn server_fill_decode_accepts_max_length() {
let value = b"0123456789abcdef0123456789abcdef";
let params = Params::decode(&encode_server_fill_value(value)).unwrap();
assert_eq!(
params.server_fill,
Some(ServerFill {
value: "0123456789abcdef0123456789abcdef".to_owned(),
})
);
}
#[test]
fn server_fill_decode_rejects_oversized_length() {
let value = b"0123456789abcdef0123456789abcdefx";
assert_eq!(
Params::decode(&encode_server_fill_value(value)),
Err(ProtoError::ParameterLengthTooLarge { tag: 9, length: 33 })
);
}
#[test] #[test]
fn server_fill_tag_and_length_are_encoded_before_utf8_bytes() { fn server_fill_tag_and_length_are_encoded_before_utf8_bytes() {
let params = Params { let params = Params {