Skip to content

Commit ea7ecf3

Browse files
LPardueghedo
authored andcommitted
lib: limit queued PATH_CHALLENGE frames
Fixes CVE-2023-6193
1 parent af368e9 commit ea7ecf3

File tree

2 files changed

+261
-13
lines changed

2 files changed

+261
-13
lines changed

quiche/src/lib.rs

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,9 @@ const MAX_SEND_UDP_PAYLOAD_SIZE: usize = 1200;
457457
// The default length of DATAGRAM queues.
458458
const DEFAULT_MAX_DGRAM_QUEUE_LEN: usize = 0;
459459

460+
// The default length of PATH_CHALLENGE receive queue.
461+
const DEFAULT_MAX_PATH_CHALLENGE_RX_QUEUE_LEN: usize = 3;
462+
460463
// The DATAGRAM standard recommends either none or 65536 as maximum DATAGRAM
461464
// frames size. We enforce the recommendation for forward compatibility.
462465
const MAX_DGRAM_FRAME_SIZE: u64 = 65536;
@@ -718,6 +721,8 @@ pub struct Config {
718721
dgram_recv_max_queue_len: usize,
719722
dgram_send_max_queue_len: usize,
720723

724+
path_challenge_recv_max_queue_len: usize,
725+
721726
max_send_udp_payload_size: usize,
722727

723728
max_connection_window: u64,
@@ -780,6 +785,9 @@ impl Config {
780785
dgram_recv_max_queue_len: DEFAULT_MAX_DGRAM_QUEUE_LEN,
781786
dgram_send_max_queue_len: DEFAULT_MAX_DGRAM_QUEUE_LEN,
782787

788+
path_challenge_recv_max_queue_len:
789+
DEFAULT_MAX_PATH_CHALLENGE_RX_QUEUE_LEN,
790+
783791
max_send_udp_payload_size: MAX_SEND_UDP_PAYLOAD_SIZE,
784792

785793
max_connection_window: MAX_CONNECTION_WINDOW,
@@ -1194,6 +1202,16 @@ impl Config {
11941202
self.dgram_send_max_queue_len = send_queue_len;
11951203
}
11961204

1205+
/// Configures the max number of queued received PATH_CHALLENGE frames.
1206+
///
1207+
/// When an endpoint receives a PATH_CHALLENGE frame and the queue is full,
1208+
/// the frame is discarded.
1209+
///
1210+
/// The default is 3.
1211+
pub fn set_path_challenge_recv_max_queue_len(&mut self, queue_len: usize) {
1212+
self.path_challenge_recv_max_queue_len = queue_len;
1213+
}
1214+
11971215
/// Sets the maximum size of the connection window.
11981216
///
11991217
/// The default value is MAX_CONNECTION_WINDOW (24MBytes).
@@ -1268,6 +1286,12 @@ pub struct Connection {
12681286
/// The path manager.
12691287
paths: path::PathMap,
12701288

1289+
/// PATH_CHALLENGE receive queue max length.
1290+
path_challenge_recv_max_queue_len: usize,
1291+
1292+
/// Total number of received PATH_CHALLENGE frames.
1293+
path_challenge_rx_count: u64,
1294+
12711295
/// List of supported application protocols.
12721296
application_protos: Vec<Vec<u8>>,
12731297

@@ -1720,7 +1744,13 @@ impl Connection {
17201744

17211745
let recovery_config = recovery::RecoveryConfig::from_config(config);
17221746

1723-
let mut path = path::Path::new(local, peer, &recovery_config, true);
1747+
let mut path = path::Path::new(
1748+
local,
1749+
peer,
1750+
&recovery_config,
1751+
config.path_challenge_recv_max_queue_len,
1752+
true,
1753+
);
17241754
// If we did stateless retry assume the peer's address is verified.
17251755
path.verified_peer_address = odcid.is_some();
17261756
// Assume clients validate the server's address implicitly.
@@ -1766,6 +1796,9 @@ impl Connection {
17661796
recovery_config,
17671797

17681798
paths,
1799+
path_challenge_recv_max_queue_len: config
1800+
.path_challenge_recv_max_queue_len,
1801+
path_challenge_rx_count: 0,
17691802

17701803
application_protos: config.application_protos.clone(),
17711804

@@ -6284,6 +6317,7 @@ impl Connection {
62846317
stopped_stream_count_local: self.stopped_stream_local_count,
62856318
reset_stream_count_remote: self.reset_stream_remote_count,
62866319
stopped_stream_count_remote: self.stopped_stream_remote_count,
6320+
path_challenge_rx_count: self.path_challenge_rx_count,
62876321
}
62886322
}
62896323

@@ -7009,6 +7043,8 @@ impl Connection {
70097043
},
70107044

70117045
frame::Frame::PathChallenge { data } => {
7046+
self.path_challenge_rx_count += 1;
7047+
70127048
self.paths
70137049
.get_mut(recv_path_id)?
70147050
.on_challenge_received(data);
@@ -7301,8 +7337,13 @@ impl Connection {
73017337
}
73027338

73037339
// This is a new path using an unassigned CID; create it!
7304-
let mut path =
7305-
path::Path::new(info.to, info.from, &self.recovery_config, false);
7340+
let mut path = path::Path::new(
7341+
info.to,
7342+
info.from,
7343+
&self.recovery_config,
7344+
self.path_challenge_recv_max_queue_len,
7345+
false,
7346+
);
73067347

73077348
path.max_send_bytes = buf_len * MAX_AMPLIFICATION_FACTOR;
73087349
path.active_scid_seq = Some(in_scid_seq);
@@ -7424,8 +7465,13 @@ impl Connection {
74247465
.ok_or(Error::OutOfIdentifiers)?
74257466
};
74267467

7427-
let mut path =
7428-
path::Path::new(local_addr, peer_addr, &self.recovery_config, false);
7468+
let mut path = path::Path::new(
7469+
local_addr,
7470+
peer_addr,
7471+
&self.recovery_config,
7472+
self.path_challenge_recv_max_queue_len,
7473+
false,
7474+
);
74297475
path.active_dcid_seq = Some(dcid_seq);
74307476

74317477
let pid = self
@@ -7532,6 +7578,9 @@ pub struct Stats {
75327578

75337579
/// The number of streams stopped by remote.
75347580
pub stopped_stream_count_remote: u64,
7581+
7582+
/// The total number of PATH_CHALLENGE frames that were received.
7583+
pub path_challenge_rx_count: u64,
75357584
}
75367585

75377586
impl std::fmt::Debug for Stats {
@@ -15300,6 +15349,9 @@ mod tests {
1530015349
};
1530115350
assert_eq!(pipe.server.recv(&mut buf[..sent], ri), Ok(sent));
1530215351

15352+
let stats = pipe.server.stats();
15353+
assert_eq!(stats.path_challenge_rx_count, 1);
15354+
1530315355
// A non-existing 4-tuple raises an InvalidState.
1530415356
let client_addr_3 = "127.0.0.1:9012".parse().unwrap();
1530515357
let server_addr_2 = "127.0.0.1:9876".parse().unwrap();
@@ -15341,6 +15393,9 @@ mod tests {
1534115393
};
1534215394
assert_eq!(pipe.server.recv(&mut buf[..sent], ri), Ok(sent));
1534315395

15396+
let stats = pipe.server.stats();
15397+
assert_eq!(stats.path_challenge_rx_count, 2);
15398+
1534415399
// STREAM frame on active path.
1534515400
let (sent, si) = pipe
1534615401
.client
@@ -15355,6 +15410,9 @@ mod tests {
1535515410
};
1535615411
assert_eq!(pipe.server.recv(&mut buf[..sent], ri), Ok(sent));
1535715412

15413+
let stats = pipe.server.stats();
15414+
assert_eq!(stats.path_challenge_rx_count, 2);
15415+
1535815416
// PATH_CHALLENGE
1535915417
let (sent, si) = pipe
1536015418
.client
@@ -15370,6 +15428,9 @@ mod tests {
1537015428
};
1537115429
assert_eq!(pipe.server.recv(&mut buf[..sent], ri), Ok(sent));
1537215430

15431+
let stats = pipe.server.stats();
15432+
assert_eq!(stats.path_challenge_rx_count, 3);
15433+
1537315434
// STREAM frame on active path.
1537415435
let (sent, si) = pipe
1537515436
.client
@@ -15419,6 +15480,9 @@ mod tests {
1541915480
v2.sort();
1542015481

1542115482
assert_eq!(v1, v2);
15483+
15484+
let stats = pipe.server.stats();
15485+
assert_eq!(stats.path_challenge_rx_count, 3);
1542215486
}
1542315487

1542415488
#[test]

0 commit comments

Comments
 (0)