Skip to content

Commit 53143ad

Browse files
Ralithdjc
authored andcommitted
Limit volume of unacked stream data
This prevents a memory exhaustion DoS if a peer issues large amounts of flow control credit without acknowledging stream data.
1 parent 1585247 commit 53143ad

File tree

2 files changed

+23
-3
lines changed

2 files changed

+23
-3
lines changed

quinn-proto/src/connection.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,14 @@ pub struct Connection {
5858
blocked_streams: FnvHashSet<StreamId>,
5959
/// Limit on outgoing data, dictated by peer
6060
max_data: u64,
61+
/// Sum of current offsets of all send streams.
6162
data_sent: u64,
62-
/// Sum of end offsets of all streams. Includes gaps, so it's an upper bound.
63+
/// Sum of end offsets of all receive streams. Includes gaps, so it's an upper bound.
6364
data_recvd: u64,
6465
/// Limit on incoming data
6566
local_max_data: u64,
67+
/// Stream data we're sending that hasn't been acknowledged or reset yet
68+
unacked_data: u64,
6669
client_config: Option<ClientConfig>,
6770
/// ConnectionId sent by this client on the first Initial, if a Retry was received.
6871
orig_rem_cid: Option<ConnectionId>,
@@ -213,6 +216,7 @@ impl Connection {
213216
data_sent: 0,
214217
data_recvd: 0,
215218
local_max_data: config.receive_window as u64,
219+
unacked_data: 0,
216220
client_config,
217221
orig_rem_cid: None,
218222
lost_packets: 0,
@@ -503,6 +507,7 @@ impl Connection {
503507
continue;
504508
};
505509
ss.bytes_in_flight -= frame.data.len() as u64;
510+
self.unacked_data -= frame.data.len() as u64;
506511
if ss.state == stream::SendState::DataSent && ss.bytes_in_flight == 0 {
507512
ss.state = stream::SendState::DataRecvd;
508513
self.maybe_cleanup(frame.id);
@@ -802,6 +807,7 @@ impl Connection {
802807
ss.offset += data.len() as u64;
803808
ss.bytes_in_flight += data.len() as u64;
804809
self.data_sent += data.len() as u64;
810+
self.unacked_data += data.len() as u64;
805811
self.space_mut(SpaceId::Data)
806812
.pending
807813
.stream
@@ -2144,6 +2150,7 @@ impl Connection {
21442150
.get(&stream.id)
21452151
.map_or(true, |s| s.send().unwrap().state.was_reset())
21462152
{
2153+
self.unacked_data -= stream.data.len() as u64;
21472154
continue;
21482155
}
21492156
let len = cmp::min(
@@ -2649,7 +2656,9 @@ impl Connection {
26492656
}
26502657

26512658
fn blocked(&self) -> bool {
2652-
self.data_sent >= self.max_data || self.congestion_blocked()
2659+
self.data_sent >= self.max_data
2660+
|| self.congestion_blocked()
2661+
|| self.unacked_data >= self.config.send_window
26532662
}
26542663

26552664
fn decrypt_packet(
@@ -2799,7 +2808,10 @@ impl Connection {
27992808
}
28002809
};
28012810

2802-
let conn_budget = self.max_data - self.data_sent;
2811+
let conn_budget = cmp::min(
2812+
self.max_data - self.data_sent,
2813+
self.config.send_window - self.unacked_data,
2814+
);
28032815
let n = conn_budget.min(stream_budget).min(data.len() as u64) as usize;
28042816
self.queue_stream_data(stream, (&data[0..n]).into());
28052817
trace!(

quinn-proto/src/endpoint.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,13 @@ pub struct TransportConfig {
830830
/// desired throughput. Larger values can be useful to allow maximum throughput within a
831831
/// stream while another is blocked.
832832
pub receive_window: u64,
833+
/// Maximum number of bytes to transmit to a peer without acknowledgment
834+
///
835+
/// Provides an upper bound on memory when communicating with peers that issue large amounts of
836+
/// flow control credit. Endpoints that wish to handle large numbers of connections robustly
837+
/// should take care to set this low enough to guarantee memory exhaustion does not occur if
838+
/// every connection uses the entire window.
839+
pub send_window: u64,
833840

834841
/// Maximum number of tail loss probes before an RTO fires.
835842
pub max_tlps: u32,
@@ -883,6 +890,7 @@ impl Default for TransportConfig {
883890
idle_timeout: 10,
884891
stream_receive_window: STREAM_RWND,
885892
receive_window: 8 * STREAM_RWND,
893+
send_window: 8 * STREAM_RWND,
886894

887895
max_tlps: 2,
888896
packet_threshold: 3,

0 commit comments

Comments
 (0)