From 9eb4abe49e9e828de463d200dffde9b521bd8ce7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 1 Aug 2026 03:40:04 +0000 Subject: [PATCH] Fix RAR5 async decompression corruption in UnpWriteBufAsync Three bugs in the async version of UnpWriteBuf (used only in the async decompression path) were causing data corruption when decompressing RAR5 archives containing executable filters (E8/E8E9/ARM) whose blocks spanned the 4MB write boundary (UNPACK_MAX_WRITE): 1. WrittenFileSize was only incremented inside `if (OutMem != null)`, but should always be incremented after ApplyFilter (matching sync behavior). 2. UnpSomeRead = true was missing from the filter processing path. 3. In the NotAllFiltersProcessed else branch (filter intersects write border): - WrPtr was not updated to WrittenBorder, corrupting the window pointer used to compute WriteBorder on the next flush cycle - Remaining filters had NextWindow set to true (inverted logic), when sync correctly sets them to false so they are processed next time All three changes align the async path with the sync UnpWriteBuf logic. --- global.json | 2 +- .../Rar/UnpackV2017/Unpack.unpack50_async.cs | 22 ++++++++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/global.json b/global.json index f5d6141c..8a7554cf 100644 --- a/global.json +++ b/global.json @@ -3,4 +3,4 @@ "version": "10.0.301", "rollForward": "disable" } -} +} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_async.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_async.cs index cabce8cf..fbb2d607 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_async.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_async.cs @@ -394,28 +394,34 @@ internal partial class Unpack { await UnpIO_UnpWriteAsync(OutMem, 0, BlockLength, cancellationToken) .ConfigureAwait(false); - WrittenFileSize += BlockLength; } + UnpSomeRead = true; + WrittenFileSize += BlockLength; WrittenBorder = BlockEnd; WriteSizeLeft = (UnpPtr - WrittenBorder) & MaxWinMask; } } else { - NotAllFiltersProcessed = true; + // Current filter intersects the window write border, so we adjust + // the window border to process this filter next time, not now. + WrPtr = WrittenBorder; + + // Since Filter start position can only increase, we quit processing + // all following filters for this data block and reset 'NextWindow' + // flag for them. for (var J = I; J < Filters.Count; J++) { var fltj = Filters[J]; - if ( - fltj.Type != FILTER_NONE - && fltj.NextWindow == false - && ((fltj.BlockStart - WrPtr) & MaxWinMask) < FullWriteSize - ) + if (fltj.Type != FILTER_NONE) { - fltj.NextWindow = true; + fltj.NextWindow = false; } } + + // Do not write data left after current filter now. + NotAllFiltersProcessed = true; break; } }