From 423844de8aa22c286a76e5b837ed7d69daf02da1 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Mon, 6 Oct 2025 19:20:03 +0000 Subject: [PATCH] Correct EOF handling in stream read in `ChaChaDualPolyReadAdapter` When `ChaChaDualPolyReadAdapter` encounters an EOF (`Read::read` returns `Ok(0)`) while trying to drain the stream (even though the `FixedLengthReader` thinks it has available space) we'll end up infinite-looping trying to drain the stream looking for `Read::read` to return an `Err` (which it won't). The fix is, of course, simple, to detect the EOF signal. Found by the `onion_message_target` fuzzer which @dergoegge ran. Thanks to @morehouse for digging deeper on the specific fuzz test case and thoroughly reporting the underlying causes. Fixes #4139. --- lightning/src/crypto/streams.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/lightning/src/crypto/streams.rs b/lightning/src/crypto/streams.rs index b631b7bd645..c406e933bc9 100644 --- a/lightning/src/crypto/streams.rs +++ b/lightning/src/crypto/streams.rs @@ -130,7 +130,10 @@ impl LengthReadableArgs<([u8; 32], [u8; 32])> for ChaChaDualPolyRea let readable: T = Readable::read(&mut chacha_stream)?; while chacha_stream.read.bytes_remain() { let mut buf = [0; 256]; - chacha_stream.read(&mut buf)?; + if chacha_stream.read(&mut buf)? == 0 { + // Reached EOF + return Err(DecodeError::ShortRead); + } } let read_len = chacha_stream.read_len; @@ -344,4 +347,20 @@ mod tests { // This also serves to test the `option: $trait` variant of the `_decode_tlv` ser macro. do_chacha_stream_adapters_ser_macros().unwrap() } + + #[test] + fn short_read_chacha_dual_read_adapter() { + // Previously, if we attempted to read from a ChaChaDualPolyReadAdapter but the object + // being read is shorter than the available buffer while the buffer passed to + // ChaChaDualPolyReadAdapter itself always thinks it has room, we'd end up + // infinite-looping as we didn't handle `Read::read`'s 0 return values at EOF. + let mut stream = &[0; 1024][..]; + let mut too_long_stream = FixedLengthReader::new(&mut stream, 2048); + let keys = ([42; 32], [99; 32]); + let res = super::ChaChaDualPolyReadAdapter::::read(&mut too_long_stream, keys); + match res { + Ok(_) => panic!(), + Err(e) => assert_eq!(e, DecodeError::ShortRead), + } + } }