EXT4: guard directory-entry parsing against out-of-bounds reads on malformed images - #848
Open
iabdullah215 wants to merge 1 commit into
Open
EXT4: guard directory-entry parsing against out-of-bounds reads on malformed images#848iabdullah215 wants to merge 1 commit into
iabdullah215 wants to merge 1 commit into
Conversation
…lformed images EXT4.EXT4Reader.getDirEntries parses a directory block from an ext4 image, which is untrusted input. Two subdata reads used attacker-controlled offsets without bounds checks: - the fixed 8-byte entry header was read whenever offset < dirTree.count, so a recordLength that leaves a short tail runs past the block; and - the entry name was read as offset+8 ..< offset+8+nameLength without validating nameLength against the block length or the record length. A malformed directory block therefore traps the reader (Data.subdata precondition failure, SIGTRAP), a denial of service. Add bounds guards mirroring the existing recordLength check so parsing stops instead of trapping, and add regression tests for the header, record-consistency, and block-bounds cases (each crashes the reader without the fix; existing EXT4 reader tests still pass).
iabdullah215
force-pushed
the
harden-ext4-direntry-bounds
branch
from
August 24, 2026 07:57
751d54d to
2a7fea6
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.
What
Add bounds checks to
EXT4.EXT4Reader.getDirEntries(inSources/ContainerizationEXT4/EXT4+Reader.swift) so that a malformed ext4 directory block can no longer driveData.subdatapast the end of the block buffer.Why
An ext4 image is untrusted input (a distributed / user-supplied rootfs block), so the reader must tolerate hostile on-disk structures. The directory-entry parse loop performed two reads at attacker-controlled offsets without bounds checks:
offset < dirTree.count. A craftedrecordLengththat advancesoffsetto within 1–7 bytes of the end makes the header read (offset ..< offset + entrySize) run past the block.offset + 8 ..< offset + 8 + nameLength, butnameLength(aUInt8taken straight from the image) was never validated against the block length, andrecordLengthwas only checked>= entrySize, not>= entrySize + nameLength.Because
Data.subdata(in:)has a precondition on its range, either case traps the process (SIGTRAP) rather than returning an error, i.e. a malformed image is a denial of service against any caller that lists a directory.Fix
Two guards inside the loop, consistent with the existing
recordLength >= entrySizeguard (stop parsing the block instead of trapping):offset + entrySize <= dirTree.countbefore loading the fixed header.recordLength >= entrySize + nameLengthandnameStart + nameLength <= dirTree.countbefore reading the name.Well-formed images are unaffected: their entries tile the block exactly and always satisfy
recordLength >= 8 + nameLengthwith the name inside the block.Testing
New
Tests/ContainerizationEXT4Tests/TestEXT4Reader+DirEntryBounds.swift:wellFormedBlockParsesAllEntries— regression: the guards don't reject valid entries.shortHeaderTailDoesNotTrap— header-bounds guard.nameLongerThanRecordDoesNotTrap— record-consistency guard.nameRunningPastBlockDoesNotTrap— block-bounds guard.Each of the three malformed-block tests crashes the reader (SIGTRAP) without this change and passes with it; all existing
EXT4PathIOTestsstill pass.Notes
getDirEntrieswasprivate; it's changed tointernalso the test can feed it crafted directory blocks directly. No public API change. Happy to keep it private and instead test via a corrupted on-disk image if preferred.break(lenient, matching the adjacentrecordLengthguard). If you'd rather surface corruption, they could throw instead, let me know your preference.Related
xattrread loop bounds #658 (Fix thexattrread loop bounds) bounded a neighboring read loop in the same reader but left the directory-entry loop unguarded; this PR adds the analogous bounds there.