Skip to content

Commit bb9cc42

Browse files
authored
Fix and harden deferred channel EOF/CLOSE replay after rekey (#670)
1 parent 6270229 commit bb9cc42

2 files changed

Lines changed: 245 additions & 17 deletions

File tree

russh/src/lib_inner.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,13 @@ impl ChannelParams {
482482
self.recipient_maximum_packet_size = c.maximum_packet_size;
483483
self.confirmed = true;
484484
}
485+
486+
pub(crate) fn take_pending_controls(&mut self) -> (bool, bool) {
487+
(
488+
std::mem::take(&mut self.pending_eof),
489+
std::mem::take(&mut self.pending_close),
490+
)
491+
}
485492
}
486493

487494
/// Returns `f(val)` if `val` it is [Some], or a forever pending [Future] if it is [None].

russh/src/session.rs

Lines changed: 238 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ impl<C> Debug for CommonSession<C> {
9797
}
9898
}
9999

100+
#[must_use]
100101
#[derive(Debug, Clone, Copy)]
101102
pub(crate) enum ChannelFlushResult {
102103
Incomplete {
@@ -115,11 +116,12 @@ impl ChannelFlushResult {
115116
ChannelFlushResult::Complete { wrote, .. } => *wrote,
116117
}
117118
}
118-
pub(crate) fn complete(wrote: usize, channel: &ChannelParams) -> Self {
119+
pub(crate) fn complete(wrote: usize, channel: &mut ChannelParams) -> Self {
120+
let (pending_eof, pending_close) = channel.take_pending_controls();
119121
ChannelFlushResult::Complete {
120122
wrote,
121-
pending_eof: channel.pending_eof,
122-
pending_close: channel.pending_close,
123+
pending_eof,
124+
pending_close,
123125
}
124126
}
125127
}
@@ -352,23 +354,19 @@ impl Encrypted {
352354
}
353355

354356
pub fn flush_pending(&mut self, channel: ChannelId) -> Result<usize, crate::Error> {
355-
let mut pending_size = 0;
356-
let mut maybe_flush_result = Option::<ChannelFlushResult>::None;
357-
358-
if let Some(channel) = self.channels.get_mut(&channel) {
359-
let flush_result = Self::flush_channel(&mut self.write, channel)?;
360-
pending_size += flush_result.wrote();
361-
maybe_flush_result = Some(flush_result);
362-
}
363-
if let Some(flush_result) = maybe_flush_result {
364-
self.handle_flushed_channel(channel, flush_result)?
365-
}
366-
Ok(pending_size)
357+
let flush_result = match self.channels.get_mut(&channel) {
358+
Some(ch) => Self::flush_channel(&mut self.write, ch)?,
359+
None => return Ok(0),
360+
};
361+
let wrote = flush_result.wrote();
362+
self.handle_flushed_channel(channel, flush_result)?;
363+
Ok(wrote)
367364
}
368365

369366
pub fn flush_all_pending(&mut self) -> Result<(), crate::Error> {
370-
for channel in self.channels.values_mut() {
371-
Self::flush_channel(&mut self.write, channel)?;
367+
let channel_ids: Vec<ChannelId> = self.channels.keys().copied().collect();
368+
for channel_id in channel_ids {
369+
self.flush_pending(channel_id)?;
372370
}
373371
Ok(())
374372
}
@@ -616,3 +614,226 @@ pub(crate) enum GlobalRequestResponse {
616614
StreamLocalForward(oneshot::Sender<bool>),
617615
CancelStreamLocalForward(oneshot::Sender<bool>),
618616
}
617+
618+
#[cfg(test)]
619+
mod tests {
620+
use std::collections::{HashMap, VecDeque};
621+
use std::num::Wrapping;
622+
623+
use byteorder::{BigEndian, ByteOrder};
624+
use bytes::Bytes;
625+
626+
use super::{Encrypted, EncryptedState, Exchange};
627+
use crate::compression::{Compression, Decompress};
628+
use crate::kex::{KEXES, NONE};
629+
use crate::{ChannelId, ChannelParams, CryptoVec, mac, msg};
630+
631+
fn test_encrypted() -> Encrypted {
632+
Encrypted {
633+
state: EncryptedState::Authenticated,
634+
exchange: Some(Exchange::default()),
635+
kex: KEXES.get(&NONE).unwrap().make(),
636+
key: 0,
637+
client_mac: mac::NONE,
638+
server_mac: mac::NONE,
639+
session_id: CryptoVec::new(),
640+
channels: HashMap::new(),
641+
last_channel_id: Wrapping(0),
642+
write: Vec::new(),
643+
write_cursor: 0,
644+
last_rekey: russh_util::time::Instant::now(),
645+
server_compression: Compression::None,
646+
client_compression: Compression::None,
647+
decompress: Decompress::None,
648+
rekey_wanted: false,
649+
received_extensions: Vec::new(),
650+
extension_info_awaiters: HashMap::new(),
651+
}
652+
}
653+
654+
fn test_channel(
655+
sender_channel: ChannelId,
656+
recipient_channel: u32,
657+
pending_eof: bool,
658+
pending_close: bool,
659+
) -> ChannelParams {
660+
ChannelParams {
661+
recipient_channel,
662+
sender_channel,
663+
recipient_window_size: 1024,
664+
sender_window_size: 1024,
665+
recipient_maximum_packet_size: 1024,
666+
sender_maximum_packet_size: 1024,
667+
confirmed: true,
668+
wants_reply: false,
669+
pending_data: VecDeque::from([(Bytes::from_static(b"hello"), None, 0)]),
670+
pending_eof,
671+
pending_close,
672+
}
673+
}
674+
675+
fn packet_types(buf: &[u8]) -> Vec<u8> {
676+
let mut packet_types = Vec::new();
677+
let mut cursor = 0;
678+
679+
while cursor < buf.len() {
680+
let packet_len = BigEndian::read_u32(&buf[cursor..cursor + 4]) as usize;
681+
packet_types.push(buf[cursor + 4]);
682+
cursor += 4 + packet_len;
683+
}
684+
685+
packet_types
686+
}
687+
688+
fn test_channel_windowed(
689+
sender_channel: ChannelId,
690+
recipient_channel: u32,
691+
window_size: u32,
692+
pending_eof: bool,
693+
pending_close: bool,
694+
) -> ChannelParams {
695+
ChannelParams {
696+
recipient_channel,
697+
sender_channel,
698+
recipient_window_size: window_size,
699+
sender_window_size: 1024,
700+
recipient_maximum_packet_size: 1024,
701+
sender_maximum_packet_size: 1024,
702+
confirmed: true,
703+
wants_reply: false,
704+
pending_data: VecDeque::from([(Bytes::from_static(b"hello"), None, 0)]),
705+
pending_eof,
706+
pending_close,
707+
}
708+
}
709+
710+
// flush_pending (single-channel path)
711+
712+
#[test]
713+
fn flush_pending_replays_deferred_eof_once() {
714+
let channel_id = ChannelId(10);
715+
let mut encrypted = test_encrypted();
716+
encrypted
717+
.channels
718+
.insert(channel_id, test_channel(channel_id, 42, true, false));
719+
720+
encrypted.flush_pending(channel_id).unwrap();
721+
assert_eq!(
722+
packet_types(&encrypted.write),
723+
vec![msg::CHANNEL_DATA, msg::CHANNEL_EOF]
724+
);
725+
assert!(!encrypted.channels[&channel_id].pending_eof);
726+
727+
// Second flush must not re-emit EOF.
728+
encrypted.flush_pending(channel_id).unwrap();
729+
assert_eq!(
730+
packet_types(&encrypted.write),
731+
vec![msg::CHANNEL_DATA, msg::CHANNEL_EOF]
732+
);
733+
}
734+
735+
#[test]
736+
fn flush_pending_replays_deferred_close_and_removes_channel() {
737+
let channel_id = ChannelId(11);
738+
let mut encrypted = test_encrypted();
739+
encrypted
740+
.channels
741+
.insert(channel_id, test_channel(channel_id, 43, true, true));
742+
743+
encrypted.flush_pending(channel_id).unwrap();
744+
assert_eq!(
745+
packet_types(&encrypted.write),
746+
vec![msg::CHANNEL_DATA, msg::CHANNEL_EOF, msg::CHANNEL_CLOSE]
747+
);
748+
assert!(!encrypted.channels.contains_key(&channel_id));
749+
}
750+
751+
#[test]
752+
fn flush_pending_no_controls_when_incomplete() {
753+
// Window smaller than data: flush is incomplete, EOF/CLOSE must not be sent.
754+
let channel_id = ChannelId(12);
755+
let mut encrypted = test_encrypted();
756+
encrypted.channels.insert(
757+
channel_id,
758+
test_channel_windowed(channel_id, 44, 3, true, true),
759+
);
760+
761+
encrypted.flush_pending(channel_id).unwrap();
762+
// Only partial data fits; no EOF or CLOSE yet.
763+
assert_eq!(packet_types(&encrypted.write), vec![msg::CHANNEL_DATA]);
764+
assert!(encrypted.channels.contains_key(&channel_id));
765+
assert!(encrypted.channels[&channel_id].pending_eof);
766+
assert!(encrypted.channels[&channel_id].pending_close);
767+
}
768+
769+
// flush_all_pending (multi-channel path)
770+
771+
#[test]
772+
fn flush_all_pending_replays_deferred_eof_once() {
773+
let channel_id = ChannelId(1);
774+
let mut encrypted = test_encrypted();
775+
encrypted
776+
.channels
777+
.insert(channel_id, test_channel(channel_id, 42, true, false));
778+
779+
encrypted.flush_all_pending().unwrap();
780+
assert_eq!(
781+
packet_types(&encrypted.write),
782+
vec![msg::CHANNEL_DATA, msg::CHANNEL_EOF]
783+
);
784+
assert!(!encrypted.channels[&channel_id].pending_eof);
785+
786+
encrypted.flush_all_pending().unwrap();
787+
assert_eq!(
788+
packet_types(&encrypted.write),
789+
vec![msg::CHANNEL_DATA, msg::CHANNEL_EOF]
790+
);
791+
}
792+
793+
#[test]
794+
fn flush_all_pending_replays_deferred_close_and_removes_channel() {
795+
let channel_id = ChannelId(2);
796+
let mut encrypted = test_encrypted();
797+
encrypted
798+
.channels
799+
.insert(channel_id, test_channel(channel_id, 43, true, true));
800+
801+
encrypted.flush_all_pending().unwrap();
802+
assert_eq!(
803+
packet_types(&encrypted.write),
804+
vec![msg::CHANNEL_DATA, msg::CHANNEL_EOF, msg::CHANNEL_CLOSE]
805+
);
806+
assert!(!encrypted.channels.contains_key(&channel_id));
807+
}
808+
809+
#[test]
810+
fn flush_all_pending_handles_multiple_channels_independently() {
811+
let eof_only = ChannelId(3);
812+
let close_too = ChannelId(4);
813+
let mut encrypted = test_encrypted();
814+
encrypted
815+
.channels
816+
.insert(eof_only, test_channel(eof_only, 50, true, false));
817+
encrypted
818+
.channels
819+
.insert(close_too, test_channel(close_too, 51, true, true));
820+
821+
encrypted.flush_all_pending().unwrap();
822+
823+
// eof_only: data + EOF, channel still present
824+
assert!(encrypted.channels.contains_key(&eof_only));
825+
assert!(!encrypted.channels[&eof_only].pending_eof);
826+
827+
// close_too: data + EOF + CLOSE, channel removed
828+
assert!(!encrypted.channels.contains_key(&close_too));
829+
830+
// Combined wire output contains both sets of packets (order may vary by map iteration).
831+
let types = packet_types(&encrypted.write);
832+
assert_eq!(types.iter().filter(|&&t| t == msg::CHANNEL_DATA).count(), 2);
833+
assert_eq!(types.iter().filter(|&&t| t == msg::CHANNEL_EOF).count(), 2);
834+
assert_eq!(
835+
types.iter().filter(|&&t| t == msg::CHANNEL_CLOSE).count(),
836+
1
837+
);
838+
}
839+
}

0 commit comments

Comments
 (0)