Skip to content

Commit

Permalink
limit maximum offset that can be buffered in a crypto stream
Browse files Browse the repository at this point in the history
  • Loading branch information
ghedo committed Mar 12, 2024
1 parent 7ab42af commit 1017466
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 0 deletions.
3 changes: 3 additions & 0 deletions quiche/include/quiche.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ enum quiche_error {

// Error in key update.
QUICHE_ERR_KEY_UPDATE = -19,

// The peer sent more data in CRYPTO frames than we can buffer.
QUICHE_ERR_CRYPTO_BUFFER_EXCEEDED = -20,
};

// Returns a human readable string with the quiche version number.
Expand Down
80 changes: 80 additions & 0 deletions quiche/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,9 @@ const MAX_PROBING_TIMEOUTS: usize = 3;
// The default initial congestion window size in terms of packet count.
const DEFAULT_INITIAL_CONGESTION_WINDOW_PACKETS: usize = 10;

// The maximum data offset that can be stored in a crypto stream.
const MAX_CRYPTO_STREAM_OFFSET: u64 = 1 << 16;

/// A specialized [`Result`] type for quiche operations.
///
/// This type is used throughout quiche's public API for any operation that
Expand Down Expand Up @@ -567,6 +570,9 @@ pub enum Error {

/// Error in key update.
KeyUpdate,

/// The peer sent more data in CRYPTO frames than we can buffer.
CryptoBufferExceeded,
}

impl Error {
Expand All @@ -581,6 +587,7 @@ impl Error {
Error::FinalSize => 0x6,
Error::IdLimit => 0x09,
Error::KeyUpdate => 0xe,
Error::CryptoBufferExceeded => 0x0d,
_ => 0xa,
}
}
Expand All @@ -607,6 +614,7 @@ impl Error {
Error::IdLimit => -17,
Error::OutOfIdentifiers => -18,
Error::KeyUpdate => -19,
Error::CryptoBufferExceeded => -20,
}
}
}
Expand Down Expand Up @@ -6809,6 +6817,10 @@ impl Connection {
},

frame::Frame::Crypto { data } => {
if data.max_off() >= MAX_CRYPTO_STREAM_OFFSET {
return Err(Error::CryptoBufferExceeded);
}

// Push the data to the stream so it can be re-ordered.
self.pkt_num_spaces[epoch].crypto_stream.recv.write(data)?;

Expand Down Expand Up @@ -9215,6 +9227,74 @@ mod tests {
assert!(pipe.server.is_closed());
}

#[test]
fn crypto_limit() {
let mut buf = [0; 65535];

let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
config
.load_cert_chain_from_pem_file("examples/cert.crt")
.unwrap();
config
.load_priv_key_from_pem_file("examples/cert.key")
.unwrap();
config
.set_application_protos(&[b"proto1", b"proto2"])
.unwrap();
config.set_initial_max_data(30);
config.set_initial_max_stream_data_bidi_local(15);
config.set_initial_max_stream_data_bidi_remote(15);
config.set_initial_max_streams_bidi(3);
config.enable_early_data();
config.verify_peer(false);

// Perform initial handshake.
let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
assert_eq!(pipe.handshake(), Ok(()));

// Client send a 1-byte frame that starts from the crypto stream offset
// limit.
let frames = [frame::Frame::Crypto {
data: stream::RangeBuf::from(b"a", MAX_CRYPTO_STREAM_OFFSET, false),
}];

let pkt_type = packet::Type::Short;

let written =
testing::encode_pkt(&mut pipe.client, pkt_type, &frames, &mut buf)
.unwrap();

let active_path = pipe.server.paths.get_active().unwrap();
let info = RecvInfo {
to: active_path.local_addr(),
from: active_path.peer_addr(),
};

assert_eq!(
pipe.server.recv(&mut buf[..written], info),
Err(Error::CryptoBufferExceeded)
);

let written = match pipe.server.send(&mut buf) {
Ok((write, _)) => write,

Err(_) => unreachable!(),
};

let frames =
testing::decode_pkt(&mut pipe.client, &mut buf[..written]).unwrap();
let mut iter = frames.iter();

assert_eq!(
iter.next(),
Some(&frame::Frame::ConnectionClose {
error_code: 0x0d,
frame_type: 0,
reason: Vec::new(),
})
);
}

#[test]
fn limit_handshake_data() {
let mut config = Config::new(PROTOCOL_VERSION).unwrap();
Expand Down

0 comments on commit 1017466

Please sign in to comment.