diff --git a/src/SharpCompress/IO/SourceStream.cs b/src/SharpCompress/IO/SourceStream.cs index 0712d915c..606b0fa75 100644 --- a/src/SharpCompress/IO/SourceStream.cs +++ b/src/SharpCompress/IO/SourceStream.cs @@ -222,8 +222,26 @@ public override long Seek(long offset, SeekOrigin origin) 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 ba42f649c..7b6a94b2d 100644 --- a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs +++ b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs @@ -717,4 +717,37 @@ public void Rar_StreamValidation_ThrowsOnTruncatedStream() // 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 var archive = RarArchive.Open(fileStream, readerOptions); + + // 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 000000000..914693f88 Binary files /dev/null and b/tests/TestArchives/Archives/Rar.malformed_512byte.rar differ