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 00000000..914693f8 Binary files /dev/null and b/tests/TestArchives/Archives/Rar.malformed_512byte.rar differ