IsRarFile() breaks RewindableStream #85

Closed
opened 2026-01-29 22:06:21 +00:00 by claunia · 3 comments
Owner

Originally created by @benshoof on GitHub (Feb 18, 2016).

The recent change to the order in which archives are tested has exposed a bug in the interaction between rar code and the RewindableStream class. This bug causes certain valid uncompressed tar archives to not be opened. SharpCompress uses RewindableStream to attempt to parse a stream as several formats until one succeeds, rewinding the stream after each attempt. The rar test breaks this by contaminating the RewindableStream in response to certain files so that it can't be rewound again. This causes the next rewind attempt to silently fail and so the subsequent tar test fails because it's not operating on the start of the file. Since the rar test was last until recently, this occasional stream contamination didn't cause a problem and wouldn't have been noticed.

I don't know all of RewindableStream's goals but at a high level I get that it's a stream with an extra buffer so that you can backtrack on otherwise forward-only streams. The problem in this case is that the Position setter nukes the buffer (SetLength(0)) and then the class can't be rewound again. Depending on the input file, IsRarFile() can set the Position while attempting to parse the first header before rejecting the file as non-rar. The rar parsing code only sets Position when StreamingMode is Seekable but IsRarFile() is hard coded to always set the mode to Seekable even when it's being invoked though the Reader API. That seems like the bug right there.

The easy lowest-impact fix is to make the caller of IsRarFile() provide the streaming mode.

I found this while looking into the previous issue using the sample archive from the reporter: https://github.com/isagalaev/highlight.js/archive/8.9.1.tar.gz
It's a compressed tar and I wanted to rule out the compression code first:

gzip -d -c highlight.js-8.9.1.tar.gz > problem.tar

The resulting uncompressed tar just happens to invoke the rar codepath that trips this bug:

using (Stream stream = File.OpenRead("problem.tar"))
{
    var reader = ReaderFactory.Open(stream); // throws "Cannot determine compressed stream type..."
}
Originally created by @benshoof on GitHub (Feb 18, 2016). The recent change to the order in which archives are tested has exposed a bug in the interaction between rar code and the RewindableStream class. This bug causes certain valid uncompressed tar archives to not be opened. SharpCompress uses RewindableStream to attempt to parse a stream as several formats until one succeeds, rewinding the stream after each attempt. The rar test breaks this by contaminating the RewindableStream in response to certain files so that it can't be rewound again. This causes the next rewind attempt to silently fail and so the subsequent tar test fails because it's not operating on the start of the file. Since the rar test was last until recently, this occasional stream contamination didn't cause a problem and wouldn't have been noticed. I don't know all of RewindableStream's goals but at a high level I get that it's a stream with an extra buffer so that you can backtrack on otherwise forward-only streams. The problem in this case is that the Position setter nukes the buffer (SetLength(0)) and then the class can't be rewound again. Depending on the input file, IsRarFile() can set the Position while attempting to parse the first header before rejecting the file as non-rar. The rar parsing code only sets Position when StreamingMode is Seekable but IsRarFile() is hard coded to always set the mode to Seekable even when it's being invoked though the Reader API. That seems like the bug right there. The easy lowest-impact fix is to make the caller of IsRarFile() provide the streaming mode. I found this while looking into the previous issue using the sample archive from the reporter: https://github.com/isagalaev/highlight.js/archive/8.9.1.tar.gz It's a compressed tar and I wanted to rule out the compression code first: `gzip -d -c highlight.js-8.9.1.tar.gz > problem.tar` The resulting uncompressed tar just happens to invoke the rar codepath that trips this bug: ``` using (Stream stream = File.OpenRead("problem.tar")) { var reader = ReaderFactory.Open(stream); // throws "Cannot determine compressed stream type..." } ```
Author
Owner

@adamhathcock commented on GitHub (Feb 18, 2016):

Thanks for this. I'm trying to think on this when my brain power isn't used on work :)

@adamhathcock commented on GitHub (Feb 18, 2016): Thanks for this. I'm trying to think on this when my brain power isn't used on work :)
Author
Owner

@benshoof commented on GitHub (Feb 18, 2016):

Streams make me feel dumb! Me stream pretty one day.

@benshoof commented on GitHub (Feb 18, 2016): Streams make me feel dumb! Me stream pretty one day.
Author
Owner

@Nanook commented on GitHub (Jul 24, 2025):

RewindableStream has been replaced with a seekable buffer that rewinds safely while retaining the original position.

@Nanook commented on GitHub (Jul 24, 2025): RewindableStream has been replaced with a seekable buffer that rewinds safely while retaining the original position.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/sharpcompress#85