Repository navigation
Bound the legacy .xls container reader by the file's own size - #7
Merged
Merged
Conversation
The OLE2 reader trusted three 32-bit header fields a file controls: the number of DIFAT sectors, the number of mini FAT sectors, and a stream's declared size. A 1 KB file whose single DIFAT sector names itself as the next one, with a count of 50,000, grew the process by about 95 MB before being rejected for having no Workbook stream, and a self-referencing FAT entry under the mini FAT did the same. Since decodeBytes picks this reader by magic bytes, an .xlsx-named upload could reach it. Every chain is now bounded by the sectors the file physically holds: a DIFAT or FAT sector listed twice, or a chain longer than the file, is rejected as corrupt before it is read again, the mini FAT is walked as a checked chain, and a stream's size must fit its chain before a buffer of that size is made for it. Sound files are read as before. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.
Problem. The OLE2 (MS-CFB) reader behind
Excel.decodeBytesfor binary.xlsinput trusts three 32-bit header fields the file controls: the numberof DIFAT sectors, the number of mini FAT sectors, and a stream's declared size.
A 1,024-byte file whose only DIFAT sector names itself as the next one, with a
declared count of 50,000, allocates about 95 MB before being rejected for
having no
Workbookstream; counts are 32-bit, so that is a floor, not aceiling. A FAT entry that points a mini FAT sector at itself does the same
through
_readMiniFat. Since the reader is chosen by magic bytes, a file named.xlsxreaches it too.Measured on the audited commit (macOS arm64, process RSS delta, illustrative):
Fix. Every chain and count is bounded by the sectors the file physically
holds. A DIFAT sector or FAT sector listed twice is rejected as corrupt before
it is read again;
_chainuses that physical bound instead of_fat.length(which an inflated FAT could stretch); the mini FAT is read as a checked chain;
a stream's declared size must fit its sector chain, and a mini stream its
container, before a buffer of that size is created; the mini chain walk has a
step bound. Everything still throws
ExcelFormatException, as the classpromises for corrupt structures. Sound files read exactly as before.
Validation. New
test/xls_compound_test.dart: cyclic DIFAT, cyclic miniFAT, duplicate FAT sector, oversized declared stream size (4 GB in a 7 KB
file), and a sound regular-sector workbook. The first three fail on the current
reader and pass with the change; the oversized-size case was only run after
the change. The existing
.xlssuites (xls_reader_test,xls_formula_test,real BIFF8 fixtures) pass unchanged; full suite and
dart analyzeclean. Thebounded reproductions now reject in 0–1 ms with negligible observed RSS growth.
A comparison against 52 external
.xlsfixtures from xlrd and Apache POIdecodes the same 48 files, rejects the same four files, and produces identical
compared cell snapshots before and after the change.
🤖 Generated with Claude Code