Fix infinite loop in SourceStream.Seek for malformed archives

- Add detection for when SetStream fails during Seek operation
- Throw InvalidOperationException with clear error message instead of looping infinitely
- Add test case Rar_MalformedArchive_NoInfiniteLoop to validate fix
- All 74 RAR archive tests pass

Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-01-31 11:02:16 +00:00
parent e8f04e22ba
commit 8cd667d0c3
3 changed files with 54 additions and 2 deletions

View File

@@ -222,8 +222,26 @@ public partial class SourceStream : Stream, IStreamStack
SetStream(0);
while (_prevSize + Current.Length < pos)
{
_prevSize += Current.Length;
SetStream(_stream + 1);
var currentLength = Current.Length;
var currentStreamIndex = _stream;
_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}."
);
}
// Check if we're making progress (stream changed or has non-zero length)
if (_stream == currentStreamIndex && currentLength == 0)
{
// Stream didn't change and has zero length - infinite loop detected
throw new InvalidOperationException(
$"Cannot seek to position {pos}. Stream has zero length and no next stream available."
);
}
}
}

View File

@@ -721,4 +721,38 @@ public class RarArchiveTests : ArchiveTests
// Verify the exception message matches our expectation
Assert.Contains("unpacked file size does not match header", exception.Message);
}
/// <summary>
/// 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/XXX
/// </summary>
[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<InvalidOperationException>(() =>
{
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);
}
}

Binary file not shown.