From e67e06ebb5c2068af50d7afd1ae2e4489bd66efa Mon Sep 17 00:00:00 2001 From: DuProcess <273172371+DuProcess@users.noreply.github.com> Date: Sun, 12 Jul 2026 23:37:47 -0400 Subject: [PATCH] Clear service decoders on oversized frames --- src/exec_service.rs | 21 +++++++++++++++++++++ src/file_transfer.rs | 13 +++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/exec_service.rs b/src/exec_service.rs index 40f4f59..fec46cd 100644 --- a/src/exec_service.rs +++ b/src/exec_service.rs @@ -32,6 +32,7 @@ impl FrameDecoder { } let len = u32::from_be_bytes(self.buf[..4].try_into().unwrap()) as usize; if len > FRAME_MAX_LEN { + self.buf.clear(); bail!("exec protocol frame too large: {len} bytes"); } if self.buf.len() < 4 + len { @@ -69,3 +70,23 @@ fn encode_frame(payload: &[u8]) -> Result> { out.extend_from_slice(payload); Ok(out) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn oversized_frame_clears_decoder_state() { + let mut decoder = FrameDecoder::default(); + let too_large = ((FRAME_MAX_LEN + 1) as u32).to_be_bytes(); + assert!(decoder.push(&too_large).is_err()); + + let valid = encode_response(&ExecResponse::Exit { code: Some(0) }).unwrap(); + let frames = decoder.push(&valid).unwrap(); + assert_eq!(frames.len(), 1); + assert_eq!( + decode_response(&frames[0]).unwrap(), + ExecResponse::Exit { code: Some(0) } + ); + } +} diff --git a/src/file_transfer.rs b/src/file_transfer.rs index 3453049..379aad2 100644 --- a/src/file_transfer.rs +++ b/src/file_transfer.rs @@ -123,6 +123,7 @@ impl FrameDecoder { } let len = u32::from_be_bytes(self.buf[..4].try_into().unwrap()) as usize; if len > FRAME_MAX_LEN { + self.buf.clear(); bail!("file protocol frame too large: {len} bytes"); } if self.buf.len() < 4 + len { @@ -288,6 +289,18 @@ mod tests { assert_eq!(frames.len(), 1); } + #[test] + fn oversized_frame_clears_decoder_state() { + let mut decoder = FrameDecoder::default(); + let too_large = ((FRAME_MAX_LEN + 1) as u32).to_be_bytes(); + assert!(decoder.push(&too_large).is_err()); + + let valid = encode_response(&FileResponse::Ok).unwrap(); + let frames = decoder.push(&valid).unwrap(); + assert_eq!(frames.len(), 1); + assert_eq!(decode_response(&frames[0]).unwrap(), FileResponse::Ok); + } + #[test] fn copy_endpoint_parses_scp_style_but_not_windows_drive() { assert_eq!(