adamhathcock / sharpcompress

SharpCompress is a fully managed C# library to deal with many compression types and formats.
MIT License
2.27k stars 480 forks source link

Rar.UnpackV1.Unpack.window is being used without initialized even when archive is solid #714

Open RigoLigoRLC opened 1 year ago

RigoLigoRLC commented 1 year ago

I'm a contributor to OpenUtau (a SharpCompress user open source software) and was debugging an issue where some RAR archives can crash the app.

Application code from OpenUtau (that is crashing):

            using (var archive = ArchiveFactory.Open(ArchiveFilePath, readerOptions)) {
                textItems.Clear();
                foreach (var entry in archive.Entries.Where(entry => entry.Key.EndsWith("character.txt") || entry.Key.EndsWith("oto.ini"))) {
                    using (var stream = entry.OpenEntryStream()) { // Crashes here
                        using var reader = new StreamReader(stream, TextEncoding);
                        textItems.Add($"------ {entry.Key} ------");
                        int count = 0;
                        while (count < 256 && !reader.EndOfStream) {
                            string? line = reader.ReadLine();
                            if (!string.IsNullOrWhiteSpace(line)) {
                                textItems.Add(line);
                                count++;
                            }
                        }
                        if (!reader.EndOfStream) {
                            textItems.Add($"...");
                        }
                    }
                }
            }

With help of dnSpy I can trace it down and single step to find this statement that causes an exception in Rar.UnpackV1.Unpack.Unpack29: 图片

I know nothing about SharpCompress' internals and how RAR decompressing works but basically the code tells me that if the archive is solid then the window member is not initialized:

            if (!fileHeader.IsSolid)
            {
                Init(null);
            }

Now that this archive apparently violated this rule.

Stack trace (from OpenUtau):

at SharpCompress.Compressors.Rar.UnpackV1.Unpack.Unpack29(Boolean solid)
   at SharpCompress.Compressors.Rar.UnpackV1.Unpack.DoUnpack()
   at SharpCompress.Compressors.Rar.UnpackV1.Unpack.DoUnpack(FileHeader fileHeader, Stream readStream, Stream writeStream)
   at SharpCompress.Compressors.Rar.RarStream..ctor(IRarUnpack unpack, FileHeader fileHeader, Stream readStream)
   at SharpCompress.Archives.Rar.RarArchiveEntry.OpenEntryStream()
   at OpenUtau.App.ViewModels.SingerSetupViewModel.RefreshTextItems() in D:\contrib\OpenUtau\OpenUtau\ViewModels\SingerSetupViewModel.cs:line 143
   at OpenUtau.App.ViewModels.SingerSetupViewModel.<.ctor>b__42_8(ValueTuple`2 _) in D:\contrib\OpenUtau\OpenUtau\ViewModels\SingerSetupViewModel.cs:line 91
...

Archive causing the failure: Mediafire

adamhathcock commented 1 year ago

The issue here is that this is a SOLID archive.

In RAR, you cannot randomly access a SOLID archive like a normal archive. Therefore, you should use the RarReader/ReaderFactory interface and treat it as a forward-only stream.

https://documentation.help/WinRAR/HELPArcSolid.htm

This detection is RAR specific and isn't available in the generic usage. I probably should have a more specific exception instead of having that random null exception