fix(asar): emit integrity blocks on real block boundaries - #171
Open
divya0795 wants to merge 1 commit into
Open
Conversation
Contributor
Author
|
Compile verification: dispatched this repo's own Result: success in ~4.2 min https://github.com/divya0795/Wand-Enhancer/actions/runs/30205235457 That satisfies "your code compiles without errors" from CONTRIBUTING.md. The manual-testing-against-WeMod step remains outstanding on my side, as flagged in the PR description. |
GetFileIntegrity treated every Stream.Read result as a complete block:
while ((bytesRead = fileStream.Read(reusableBuffer, 0, reusableBuffer.Length)) > 0)
{
blockHashes.Add(ToLowerHex(blockHash.ComputeHash(reusableBuffer, 0, bytesRead)));
fileHash.AppendData(reusableBuffer, 0, bytesRead);
}
Stream.Read is permitted to return fewer bytes than requested at any point, not
only at end of file. Each short read therefore produced an undersized entry in
integrity.blocks and shifted every boundary after it, so the resulting metadata
fails Electron's validateAsarIntegrity, which requires every block except the
last to be exactly blockSize. The whole-file hash stayed correct, so only
per-block validation was affected.
Accumulate reads into the block buffer and emit a hash only once a full block
is present, flushing the remainder after the loop. Reads target the tail of the
buffer, so no copying or extra allocation is involved.
The buffer is now also replaced when a caller supplies one shorter than
BLOCK_SIZE, since a partial buffer cannot produce a valid block either way.
Note StreamingHasher already implements this accumulate-then-emit rule
correctly and is what the main WriteFileSystem path uses, which is why packing
has not been visibly affected; the defect was confined to direct
GetFileIntegrity callers. Delegating to StreamingHasher was the obvious way to
remove the duplication, but its Append copies the caller's data into its own
block buffer, so sharing one array as both read target and block buffer would
alias. Fixing the loop in place keeps the copy-free read path.
Verified by simulating the read/boundary arithmetic against Electron's rule
across full reads, mid-file short reads, single-byte reads, ragged reads, exact
multiples, a sub-block file and an empty file: the new logic matches the
expected block layout in every case and preserves the whole-file hash, while
the previous logic diverges on every short-read scenario (for example 40 blocks
instead of 5 when reads return one byte at a time).
divya0795
force-pushed
the
fix/asar-integrity-partial-reads
branch
from
July 26, 2026 14:32
4a7fcb3 to
53c880d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #170
Problem
GetFileIntegrityhashed eachStream.Readresult as if it were a complete block.Stream.Readmay return fewer bytes than requested at any point, not only at EOF, so a short read produced an undersizedintegrity.blocks[]entry and shifted every boundary after it — metadata that fails Electron'svalidateAsarIntegrity, which requires every block except the last to be exactlyblockSize.Reachable through
FileSystem.InsertFile, which calls it as theprecomputedIntegrity ?? …fallback at:175and:184. The whole-filehashwas never affected.Change
Accumulate reads into the block buffer and emit a hash only when a full block is present, flushing the remainder after the loop. Reads target the tail of the buffer (
Read(buf, blockFill, BLOCK_SIZE - blockFill)), so there is no copying and no extra allocation.Also replaces a caller-supplied buffer shorter than
BLOCK_SIZE, since such a buffer cannot produce a valid block either way. No current caller passes one.A note on reuse
StreamingHasherin the same file already implements this rule correctly, and delegating to it was the obvious way to remove the duplication. I did not, for a concrete reason:StreamingHasher.Appendcopies the caller's data into its own block buffer, so passing one array as both the read target and the shared block buffer would alias —Buffer.BlockCopy(data, src, _blockBuf, _blockFill, copy)would overwrite unread source bytes whenever_blockFill > 0. The alternatives were an extra 4 MiB allocation per file or a per-byte copy. Fixing the loop in place keeps the copy-free read path; happy to switch if you'd rather have the single implementation.Verification
No local Visual Studio/MSBuild environment, so this is not compile-verified locally. Compile evidence comes from dispatching this repo's own
.github/workflows/build.yml(fullbuild.ps1→ MSBuild,windows-latest) on my fork; run linked in a comment below.The algorithm itself I verified by simulating the read/boundary arithmetic against Electron's rule (block size scaled down, arithmetic identical):
masterand the fix agree whenever every read is full — the normal case, and why this is latent — and diverge on every short-read scenario. Whole-file hash preserved throughout.The repo has no C# test project, so there is no unit test to add here. I also have no licensed WeMod install, so CONTRIBUTING.md's "manually tested against the current WeMod version" is not satisfied — worth a maintainer check against a real packing run.