# TC153 Large Directory Block Index Validation Status: **RESOLVED - exact-source kernel validation passed** Last updated: 2026-08-09 Priority: Critical validation gap Implementation status: Source fix present, build-verified, and dynamically validated ## Problem `erofs_find_target_block()` used signed `int` values for the binary-search front, back, and midpoint indexes. A directory whose logical size describes `2147483649` 4 KiB blocks produces a final block index of `2147483648`. Converting that value to `int` can make the initial back bound negative. The search can then return `ENOENT` without reading any directory block, and the FreeBSD namecache can retain a false negative result for corrupt media. The review change uses `uint64_t` for block-search bounds and midpoints, checks the midpoint multiplication, and handles the `mid == 0` lower-bound case. The within-block search remains `uint32_t` because a validated EROFS directory block can contain only a block-sized number of entries. ## Affected Features - Cold pathname lookup in very large or maliciously sized directories. - Corruption reporting and `EINTEGRITY` propagation from directory reads. - Negative namecache correctness after a failed lookup. - Linux/FreeBSD behavioral review of EROFS's two-level directory search. Normal directories are not expected to approach this bound. The practical risk is a crafted image causing an incorrect `ENOENT`, not an ordinary mkfs image losing entries. ## Trigger Conditions All of the following are needed to exercise the original width error: 1. A directory inode is accepted with a logical block count greater than `INT_MAX`. 2. The directory layout reaches `erofs_find_target_block()` rather than being rejected by an earlier inline-data bounds check. 3. A cold lookup is issued for a name that is not satisfied by an already cached vnode or negative namecache entry. 4. The backing image is too short for the computed midpoint read, so the fixed code should return `EINTEGRITY` instead of a synthetic miss. For 4 KiB blocks, TC153 encodes a size of `8796093026304` bytes, `2147483649` blocks, and a final index of `2147483648`. ## Current Analysis The source change is mechanically narrow and has passed both repo22 build configurations. The checked multiplication protects a future block-size or index change even though the current `OFF_MAX` inode limit already bounds the product. The `mid == 0` guard prevents unsigned subtraction from wrapping when the search moves below the first block. Dynamic proof must show more than a large `st_size`. It must prove that the lookup enters the block binary search. An image rejected in `erofs_read_inode()` is not evidence for this fix, and an `ENOENT` observed after a warm negative cache is ambiguous. ## Attempts and Results ### Attempt 1: Small FLAT_INLINE image - Artifact directory: `/work/build/repo22-review-artifact.LtqNxH` - Base image: `large-dir-base.erofs`, 4096 bytes, SHA256 `973d2a1c90ac1bf776ed76a1fc6a7e88b2156fac3bf04ddbae19cfafebbd562e` - Patched image: `large-dir-intmax.erofs`, 4096 bytes, SHA256 `ce5cad3eff34ea50ca89eedf93d8ef90e6cea96858a138685fae45f39084de1f` - Recorded inode: NID 40 at byte 1280. The directory was Layout 2 (`FLAT_INLINE`). Patching only `i_size` made its declared inline tail range invalid, so inode validation rejected it before `erofs_find_target_block()`. This attempt is invalid as TC153 dynamic evidence and must never be marked PASS. ### Attempt 2: More entries to force block data - Artifact directory: `/work/build/repo22-review-artifact2.FmMTup` - Generated base: `large-dir-base.erofs`, 65536 bytes, SHA256 `50eb28351788ab1801408aca0a6fca6352755f5ca56efe6e2bb3e2fe08305663` - Directory: NID 40, 21052 bytes, still Layout 2. The generation process was interrupted before producing a patched image or a FreeBSD result. A later deterministic rerun reproduced the same base hash and showed why the approach failed: erofs-utils 1.8.6 tail-packs directories even when they span multiple data blocks. Its `noinline_data` option does not turn directory tail packing off. ### Attempt 3: Follow-up agents Multiple follow-up agents were assigned the fixture and guest validation. They either stopped while preparing the second image or returned no executed commands, files, hashes, or guest output. These attempts added no evidence and are recorded to prevent their elapsed time from being mistaken for progress. ### Attempt 4: Sparse-provider design A guest-side sparse vnode provider was considered so a valid directory could declare the complete multi-terabyte range. It was not executed. Such a scheme must prove the GEOM media size, avoid materializing terabytes, preserve valid initial directory blocks, and still force a cold read at the binary-search midpoint. No result exists for this approach. ### Attempt 5: Reproducible FLAT_PLAIN conversion The tracked generator now copies all 21052 bytes of the generated directory to appended blocks, changes only that inode to Layout 0 (`FLAT_PLAIN`), updates the image block count and CRC32C, and then applies the large logical size. Host reproduction on 2026-08-09 produced: - `large-dir-base.erofs`: 65536 bytes, SHA256 `50eb28351788ab1801408aca0a6fca6352755f5ca56efe6e2bb3e2fe08305663` - `large-dir-intmax.erofs`: 90112 bytes, SHA256 `0f90d3d57adbbbd946e41b225c1f6c464915c6abb0b13478ec9b2a318def1f72` - Patched inode: NID 40 at byte 1280, Layout 0, raw block 16. - Declared image blocks: 22. This resolved the fixture-construction problem. The qualified kernel run is recorded below. ### Attempt 6: Qualified sparse GEOM provider The G3 takeover generated a checksum-valid sparse prefix from Attempt 5 and performed one targeted FreeBSD 15 run against the exact source requested by the assignment. - Source commit: 9ae22009f23a65320730072a780998e80aa9b728. - KLD SHA256: 19ad086bd2508cbb4c57b1a51f38c93057d438b84fbcfa2e637ff2519f5bc590. - Sparse-prefix SHA256: f9337f83b1f568f6d904331a7749e6362a691055cd5af95b59bc89772f04e3b0. - Prefix qualification: NID 40, inode offset 1280, extended Layout 0, start block 16, 2147483649 directory blocks, final block index 2147483648, valid superblock checksum. - Sparse provider: 8796093091840 logical bytes, 448 allocated 512-byte sectors, and the first 90112 bytes retained the prefix SHA256. - GEOM diskinfo size: 8796093091840 bytes. - Mounted /huge: size 8796093026304, NID 40. The first two cold stat operations for /huge/missing each returned EINTEGRITY (97). Root readdir then returned three complete entries and validated all kernel/libc restart cookies. A third lookup after that readdir again returned EINTEGRITY. No lookup returned ENOENT, so no false negative namecache entry masked the corruption. Unmount, md detach, sparse-provider removal, and KLD unload all succeeded. The only new dmesg lines in the complete G3 run were expected SIGBUS child exits from the unrelated assigned mmap tests; TC153 added no diagnostic. ## Historical Remaining Validation Attempt 6 completed steps 1-5 and 7 below. The assignment mandated the exact fixed source, so the optional pre-fix comparison in step 6 was not run. 1. Regenerate the images with `tests/results/manual/2026-08-09T0124Z-final-review/prepare-fixtures.sh` and verify the hashes and Layout 0 assertion. 2. Load the exact module under test in a clean FreeBSD 15 guest. 3. Mount the 90112-byte image and confirm `/huge` reports `8796093026304` bytes. 4. With a cold parent cache, run two lookups of `/huge/missing` and capture direct command exit codes plus `truss` errno. 5. Require `EINTEGRITY` on every fixed-module lookup, including after root readdir. Confirm no negative cache changes the result. 6. Repeat with the pre-fix module to demonstrate the erroneous `ENOENT` path. 7. Record dmesg, mount/md/KLD cleanup, module and fixture hashes in a dated manual report. ## Original Acceptance Criteria TC153 can move from SHELVED to PASS only after the fixed FreeBSD KLD returns `EINTEGRITY` repeatedly from the valid Layout 0 fixture, the pre-fix behavior is distinguished, no trap or panic occurs, and complete cleanup evidence is recorded. Host fixture generation and static source review alone are insufficient. ## Resolution TC153 is PASS because the exact fixed FreeBSD KLD returned EINTEGRITY repeatedly from the valid Layout 0 fixture before and after root readdir, no trap or panic occurred, and complete hash/provider/cleanup evidence is recorded in tests/results/manual/2026-08-09T1059Z-g3/manual-test-report.md. Host fixture generation and static source review alone remain insufficient.