Skip to content

Commit 01cbb44

Browse files
fix(s2n-quic-dc): wait to insert in peer map until handshake completes (#2358)
1 parent 6c7057f commit 01cbb44

File tree

7 files changed

+79
-19
lines changed

7 files changed

+79
-19
lines changed

dc/s2n-quic-dc/src/path/secret/map.rs

+23-10
Original file line numberDiff line numberDiff line change
@@ -488,19 +488,21 @@ impl Map {
488488
Some(state.clone())
489489
}
490490

491-
pub(super) fn insert(&self, entry: Arc<Entry>) {
491+
pub(super) fn on_new_path_secrets(&self, entry: Arc<Entry>) {
492492
// On insert clear our interest in a handshake.
493493
self.state.requested_handshakes.pin().remove(&entry.peer);
494-
let id = *entry.secret.id();
495-
let peer = entry.peer;
496-
if self.state.ids.insert(id, entry.clone()).is_some() {
494+
if self.state.ids.insert(*entry.secret.id(), entry).is_some() {
497495
// FIXME: Make insertion fallible and fail handshakes instead?
498496
panic!("inserting a path secret ID twice");
499497
}
498+
}
499+
500+
pub(super) fn on_handshake_complete(&self, entry: Arc<Entry>) {
501+
let id = *entry.secret.id();
500502

501-
if let Some(prev) = self.state.peers.insert(peer, entry) {
502-
// This shouldn't happen due to the panic above, but just in case something went wrong
503-
// with the secret map we double check here.
503+
if let Some(prev) = self.state.peers.insert(entry.peer, entry) {
504+
// This shouldn't happen due to the panic in on_new_path_secrets, but just
505+
// in case something went wrong with the secret map we double check here.
504506
// FIXME: Make insertion fallible and fail handshakes instead?
505507
assert_ne!(*prev.secret.id(), id, "duplicate path secret id");
506508

@@ -546,7 +548,8 @@ impl Map {
546548
dc::testing::TEST_REHANDSHAKE_PERIOD,
547549
);
548550
let entry = Arc::new(entry);
549-
provider.insert(entry);
551+
provider.on_new_path_secrets(entry.clone());
552+
provider.on_handshake_complete(entry);
550553
}
551554

552555
(provider, ids)
@@ -573,7 +576,9 @@ impl Map {
573576
dc::testing::TEST_APPLICATION_PARAMS,
574577
dc::testing::TEST_REHANDSHAKE_PERIOD,
575578
);
576-
self.insert(Arc::new(entry));
579+
let entry = Arc::new(entry);
580+
self.on_new_path_secrets(entry.clone());
581+
self.on_handshake_complete(entry);
577582
}
578583

579584
fn send_control(&self, entry: &Entry, credentials: &Credentials, error: receiver::Error) {
@@ -1057,7 +1062,15 @@ impl dc::Path for HandshakingPath {
10571062
);
10581063
let entry = Arc::new(entry);
10591064
self.entry = Some(entry.clone());
1060-
self.map.insert(entry);
1065+
self.map.on_new_path_secrets(entry);
1066+
}
1067+
1068+
fn on_dc_handshake_complete(&mut self) {
1069+
let entry = self.entry.clone().expect(
1070+
"the dc handshake cannot be complete without \
1071+
on_peer_stateless_reset_tokens creating a map entry",
1072+
);
1073+
self.map.on_handshake_complete(entry);
10611074
}
10621075

10631076
fn on_mtu_updated(&mut self, mtu: u16) {

dc/s2n-quic-dc/src/path/secret/map/test.rs

+26-9
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,19 @@ fn cleans_after_delay() {
4141
let first = fake_entry(1);
4242
let second = fake_entry(1);
4343
let third = fake_entry(1);
44-
map.insert(first.clone());
45-
map.insert(second.clone());
44+
map.on_new_path_secrets(first.clone());
45+
map.on_handshake_complete(first.clone());
46+
map.on_new_path_secrets(second.clone());
47+
map.on_handshake_complete(second.clone());
4648

4749
assert!(map.state.ids.contains_key(first.secret.id()));
4850
assert!(map.state.ids.contains_key(second.secret.id()));
4951

5052
map.state.cleaner.clean(&map.state, 1);
5153
map.state.cleaner.clean(&map.state, 1);
5254

53-
map.insert(third.clone());
55+
map.on_new_path_secrets(third.clone());
56+
map.on_handshake_complete(third.clone());
5457

5558
assert!(!map.state.ids.contains_key(first.secret.id()));
5659
assert!(map.state.ids.contains_key(second.secret.id()));
@@ -86,9 +89,10 @@ struct Model {
8689

8790
#[derive(bolero::TypeGenerator, Debug, Copy, Clone)]
8891
enum Operation {
89-
Insert { ip: u8, path_secret_id: TestId },
92+
NewPathSecret { ip: u8, path_secret_id: TestId },
9093
AdvanceTime,
9194
ReceiveUnknown { path_secret_id: TestId },
95+
HandshakeComplete { path_secret_id: TestId },
9296
}
9397

9498
#[derive(bolero::TypeGenerator, PartialEq, Eq, Hash, Copy, Clone)]
@@ -130,13 +134,13 @@ enum Invariant {
130134
impl Model {
131135
fn perform(&mut self, operation: Operation, state: &Map) {
132136
match operation {
133-
Operation::Insert { ip, path_secret_id } => {
137+
Operation::NewPathSecret { ip, path_secret_id } => {
134138
let ip = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from([0, 0, 0, ip]), 0));
135139
let secret = path_secret_id.secret();
136140
let id = *secret.id();
137141

138142
let stateless_reset = state.state.signer.sign(&id);
139-
state.insert(Arc::new(Entry::new(
143+
state.on_new_path_secrets(Arc::new(Entry::new(
140144
ip,
141145
secret,
142146
sender::State::new(stateless_reset),
@@ -145,9 +149,16 @@ impl Model {
145149
dc::testing::TEST_REHANDSHAKE_PERIOD,
146150
)));
147151

148-
self.invariants.insert(Invariant::ContainsIp(ip));
149152
self.invariants.insert(Invariant::ContainsId(id));
150153
}
154+
Operation::HandshakeComplete { path_secret_id } => {
155+
if let Some(entry) = state.state.ids.get_by_key(&path_secret_id.id()) {
156+
if !state.state.peers.contains_key(&entry.peer) {
157+
state.on_handshake_complete(entry.clone());
158+
}
159+
self.invariants.insert(Invariant::ContainsIp(entry.peer));
160+
}
161+
}
151162
Operation::AdvanceTime => {
152163
let mut invalidated = Vec::new();
153164
self.invariants.retain(|invariant| {
@@ -232,7 +243,7 @@ fn has_duplicate_pids(ops: &[Operation]) -> bool {
232243
let mut ids = HashSet::new();
233244
for op in ops.iter() {
234245
match op {
235-
Operation::Insert {
246+
Operation::NewPathSecret {
236247
ip: _,
237248
path_secret_id,
238249
} => {
@@ -244,6 +255,10 @@ fn has_duplicate_pids(ops: &[Operation]) -> bool {
244255
Operation::ReceiveUnknown { path_secret_id: _ } => {
245256
// no-op, we're fine receiving unknown pids.
246257
}
258+
Operation::HandshakeComplete { .. } => {
259+
// no-op, a handshake complete for the same pid as a
260+
// new path secret is expected
261+
}
247262
}
248263
}
249264

@@ -320,7 +335,9 @@ fn no_memory_growth() {
320335
map.state.cleaner.stop();
321336
for idx in 0..500_000 {
322337
// FIXME: this ends up 2**16 peers in the `peers` map
323-
map.insert(fake_entry(idx as u16));
338+
let entry = fake_entry(idx as u16);
339+
map.on_new_path_secrets(entry.clone());
340+
map.on_handshake_complete(entry)
324341
}
325342
}
326343

quic/s2n-quic-core/src/dc/disabled.rs

+4
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ impl Path for () {
4545
unimplemented!()
4646
}
4747

48+
fn on_dc_handshake_complete(&mut self) {
49+
unimplemented!()
50+
}
51+
4852
fn on_mtu_updated(&mut self, _mtu: u16) {
4953
unimplemented!()
5054
}

quic/s2n-quic-core/src/dc/testing.rs

+8
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ impl MockDcEndpoint {
3434
pub struct MockDcPath {
3535
pub on_path_secrets_ready_count: u8,
3636
pub on_peer_stateless_reset_tokens_count: u8,
37+
pub on_dc_handshake_complete: u8,
3738
pub stateless_reset_tokens: Vec<stateless_reset::Token>,
3839
pub peer_stateless_reset_tokens: Vec<stateless_reset::Token>,
3940
pub mtu: u16,
@@ -69,6 +70,7 @@ impl dc::Path for MockDcPath {
6970
&mut self,
7071
_session: &impl TlsSession,
7172
) -> Result<Vec<stateless_reset::Token>, transport::Error> {
73+
debug_assert_eq!(0, self.on_path_secrets_ready_count);
7274
self.on_path_secrets_ready_count += 1;
7375
Ok(self.stateless_reset_tokens.clone())
7476
}
@@ -77,11 +79,17 @@ impl dc::Path for MockDcPath {
7779
&mut self,
7880
stateless_reset_tokens: impl Iterator<Item = &'a stateless_reset::Token>,
7981
) {
82+
debug_assert_eq!(0, self.on_peer_stateless_reset_tokens_count);
8083
self.on_peer_stateless_reset_tokens_count += 1;
8184
self.peer_stateless_reset_tokens
8285
.extend(stateless_reset_tokens);
8386
}
8487

88+
fn on_dc_handshake_complete(&mut self) {
89+
debug_assert_eq!(0, self.on_dc_handshake_complete);
90+
self.on_dc_handshake_complete += 1;
91+
}
92+
8593
fn on_mtu_updated(&mut self, mtu: u16) {
8694
self.mtu = mtu
8795
}

quic/s2n-quic-core/src/dc/traits.rs

+12
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ pub trait Path: 'static + Send {
4646
stateless_reset_tokens: impl Iterator<Item = &'a stateless_reset::Token>,
4747
);
4848

49+
/// Called when the peer has confirmed receipt of `DC_STATELESS_RESET_TOKENS`, either
50+
/// by the server sending back its own `DC_STATELESS_RESET_TOKENS` or by the client
51+
/// acknowledging the `DC_STATELESS_RESET_TOKENS` frame was received.
52+
fn on_dc_handshake_complete(&mut self);
53+
4954
/// Called when the MTU has been updated for the path
5055
fn on_mtu_updated(&mut self, mtu: u16);
5156
}
@@ -73,6 +78,13 @@ impl<P: Path> Path for Option<P> {
7378
}
7479
}
7580

81+
#[inline]
82+
fn on_dc_handshake_complete(&mut self) {
83+
if let Some(path) = self {
84+
path.on_dc_handshake_complete()
85+
}
86+
}
87+
7688
#[inline]
7789
fn on_mtu_updated(&mut self, max_datagram_size: u16) {
7890
if let Some(path) = self {

quic/s2n-quic-transport/src/dc/manager.rs

+2
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ impl<Config: endpoint::Config> Manager<Config> {
160160
if Config::ENDPOINT_TYPE.is_server() {
161161
self.stateless_reset_token_sync.send();
162162
} else {
163+
self.path.on_dc_handshake_complete();
163164
publisher.on_dc_state_changed(DcStateChanged {
164165
state: DcState::Complete,
165166
});
@@ -176,6 +177,7 @@ impl<Config: endpoint::Config> Manager<Config> {
176177
ensure!(self.state.on_stateless_reset_tokens_acked().is_ok());
177178

178179
debug_assert!(Config::ENDPOINT_TYPE.is_server());
180+
self.path.on_dc_handshake_complete();
179181
publisher.on_dc_state_changed(DcStateChanged {
180182
state: DcState::Complete,
181183
});

quic/s2n-quic-transport/src/dc/manager/tests.rs

+4
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,9 @@ fn on_peer_dc_stateless_reset_tokens<Config, Endpoint>(
149149

150150
if Config::ENDPOINT_TYPE.is_server() {
151151
assert!(manager.state.is_server_tokens_sent());
152+
assert_eq!(0, manager.path().on_dc_handshake_complete);
152153
} else {
154+
assert_eq!(1, manager.path().on_dc_handshake_complete);
153155
assert!(manager.state.is_complete());
154156
}
155157

@@ -169,6 +171,7 @@ fn on_packet_ack_client() {
169171

170172
// Client completes when it has received stateless reset tokens from the peer
171173
assert!(!manager.state.is_complete());
174+
assert_eq!(0, manager.path().on_dc_handshake_complete);
172175
}
173176

174177
#[test]
@@ -182,6 +185,7 @@ fn on_packet_ack_server() {
182185

183186
// Server completes when its stateless reset tokens are acked
184187
assert!(manager.state.is_complete());
188+
assert_eq!(1, manager.path().on_dc_handshake_complete);
185189
}
186190

187191
fn on_packet_ack<Config, Endpoint>(

0 commit comments

Comments
 (0)