From 8737b7a38e39bcf26e11fd96050748dd191d3f93 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Feb 2026 09:31:38 +0000 Subject: [PATCH] Apply infinite loop fix to SourceStream.cs and add test case Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com> --- src/SharpCompress/IO/SourceStream.cs | 22 ++++++++++-- .../SharpCompress.Test/Rar/RarArchiveTests.cs | 34 ++++++++++++++++++ .../Archives/Rar.malformed_512byte.rar | Bin 0 -> 512 bytes 3 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 tests/TestArchives/Archives/Rar.malformed_512byte.rar diff --git a/src/SharpCompress/IO/SourceStream.cs b/src/SharpCompress/IO/SourceStream.cs index 0712d915..606b0fa7 100644 --- a/src/SharpCompress/IO/SourceStream.cs +++ b/src/SharpCompress/IO/SourceStream.cs @@ -222,8 +222,26 @@ public class SourceStream : Stream, IStreamStack SetStream(0); while (_prevSize + Current.Length < pos) { - _prevSize += Current.Length; - SetStream(_stream + 1); + var currentLength = Current.Length; + _prevSize += currentLength; + + if (!SetStream(_stream + 1)) + { + // No more streams available, cannot seek to requested position + throw new InvalidOperationException( + $"Cannot seek to position {pos}. End of stream reached at position {_prevSize}." + ); + } + + // Safety check: if we have a zero-length stream and we're still not + // making progress toward the target position, we're in an invalid state + if (currentLength == 0 && Current.Length == 0) + { + // Both old and new stream have zero length - cannot make progress + throw new InvalidOperationException( + $"Cannot seek to position {pos}. Encountered zero-length streams at position {_prevSize}." + ); + } } } diff --git a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs index ba42f649..3e67dbe3 100644 --- a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs +++ b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs @@ -717,4 +717,38 @@ public class RarArchiveTests : ArchiveTests // Verify the exception message matches our expectation Assert.Contains("unpacked file size does not match header", exception.Message); } + + /// + /// Test case for malformed RAR archives that previously caused infinite loops. + /// This test verifies that attempting to read entries from a potentially malformed + /// 512-byte RAR archive throws an InvalidOperationException instead of looping infinitely. + /// See: https://github.com/adamhathcock/sharpcompress/issues/1176 + /// + [Fact] + public void Rar_MalformedArchive_NoInfiniteLoop() + { + var testFile = "Rar.malformed_512byte.rar"; + var readerOptions = new ReaderOptions { LookForHeader = true }; + + // This should throw InvalidOperationException, not hang in an infinite loop + var exception = Assert.Throws(() => + { + using var fileStream = File.Open( + Path.Combine(TEST_ARCHIVES_PATH, testFile), + FileMode.Open + ); + using IRarArchive rarFile = RarArchive.OpenArchive(fileStream, readerOptions); + using IArchive archive = rarFile; + + // Attempting to enumerate entries should throw an exception + // instead of looping infinitely + foreach (var entry in archive.Entries.Where(e => !e.IsDirectory)) + { + // This line should not be reached due to the exception + } + }); + + // Verify that the exception is related to seeking beyond available data + Assert.Contains("Cannot seek to position", exception.Message); + } } diff --git a/tests/TestArchives/Archives/Rar.malformed_512byte.rar b/tests/TestArchives/Archives/Rar.malformed_512byte.rar new file mode 100644 index 0000000000000000000000000000000000000000..914693f8840453420463ef96e01af7040c8e3ff8 GIT binary patch literal 512 zcmWGaEK-zWXE;Bhn1O+p0Rm1>s$ytRW=LaTV2EO1U|^b&lD-)vBWuhIq8OZgLwF^x zocKG3=Y6M-?1Mms{Z~&XX(UE^`~G7-k=tv0`Nw^huB5)UWtv}Q_MNnvcffkX*Ot#p zh3{sZ?6_;q-@M-Yi0b3?u&{hVr|nbkwfsGEL3FC!|2Z3a)$aOPuHJK4akAXk`deyE z4_`%<9GGCwU@_5ylYt>ghN-wld-9})L^D}qSq23L28NW(;*yxO%$(HXn54|Sn4;9; znB@H2-26Pfg2Zfw0}`?U7tTzadqc4C?Qv7#Kofx$CPvtG{0@ohnHdfoX69j> z{V8!h?}jgaa;Hr9x6%i9^UZFS#QcaZ|gQ$MqmIW{pq zjpLkhr}k~g{p0WDyElk29)HrjdE&`T(Zbf$LVxB93yTvZ7A0k{Br%+Qc7@OHqQe8d LXb-iL{-iDdA_?4L literal 0 HcmV?d00001