4.8 KiB
repo22 Cold Nested Namei Regression Report
- Date: 2026-08-08 UTC
- Baseline:
29a215dd4519579f6313b3df67d524a6b4bdf3ca - Guest: FreeBSD 15.0-RELEASE-p8 amd64, QEMU TCG
- Final module SHA-256:
50a19ea96c7414f73d92049671e66a9eacc9ff5abe27950a46d16c8e6c763baf - Result: PASS
Root Cause
FreeBSD pathname lookup passes a component as cn_nameptr plus
cn_namelen. An intermediate component is followed by / in the pathname
buffer and is not NUL-terminated at cn_namelen. The imported Linux EROFS
comparison assumed Linux dentry-name termination, ignored the supplied length,
and tested qn_name[i] == '\0' after matching the on-disk name. Therefore a
final component worked, while the same name used as an intermediate component
compared greater than the on-disk entry and returned ENOENT. Looking up the
parent as a final component first populated the FreeBSD namecache and hid the
bug on the next nested lookup.
The old error path also inserted a negative cache entry for every lookup
error, including integrity and I/O errors, which could mask later corruption
as ENOENT.
Implementation
src/namei.c- Compares pathname components by explicit length without reading beyond
cn_namelen. - Uses unsigned-byte ordering compatible with EROFS directory sorting.
- Validates the minimum block size before reading the first dirent.
- Validates the dirent-array boundary, strictly increasing name offsets, name-slot bounds, name length, and zero-only NUL padding.
- Inserts negative namecache entries only for real
ENOENTmisses.
- Compares pathname components by explicit length without reading beyond
src/dir.c- Applies the same directory-block and name-padding validation to
readdir. - Determines the actual last-name length before enforcing
EROFS_NAME_LEN, so valid full-block zero padding is accepted. - Keeps on-disk offsets unchanged and appends a synthetic
.ati_sizefordot_omitted, matching Linux EROFS. - Aligns restart positions relative to each directory block and preserves
the
i_sizecookie needed to resume the synthetic dot entry. - Initializes returned cookie-array outputs before allocation.
- Applies the same directory-block and name-padding validation to
Deterministic Fixture
prepare-fixtures.sh creates the same image twice and requires cmp success.
The base image contains a cold multi-level path and a 320-file, multi-block
directory. Fixed timestamp, ownership, UUID, worker count, and uncompressed
layout are used.
Final image hashes:
2e9fd75159011ced31646a38f942fa0822d5b3e8e19b7df55b844575fba991ea corrupt-nameoff.erofs
63d12232f9ea0dc7f7ff477d20b95a8412d191fc10bf4b67dacc47e050c379fb corrupt-padding.erofs
77f8a56f969e173d291ccbd957821f1ba284d3e54d875dbcdb9bc398024dfa5a corrupt-short-block.erofs
11064ffbb814ff41027aebc8f36e56d2f24affb7b50836948b3ffbf6d79dee0e dot-omitted.erofs
7a2c244ec10e9a43531b0e8b92e26b11f52d67bab00528b8ba2f584e20e57420 nested-repeat.erofs
7a2c244ec10e9a43531b0e8b92e26b11f52d67bab00528b8ba2f584e20e57420 nested.erofs
Build Results
./build.sh: PASS.- Final
build/erofs.koSHA-256 remained50a19ea96c7414f73d92049671e66a9eacc9ff5abe27950a46d16c8e6c763baf. nm -u build/erofs.ko | grep -w bcmp: no match.git diff --checkfor all scoped source and test files: PASS.
The final integration rerun used the same combined module and these guest commands:
cd /root/repo22-namei
cc -Wall -Wextra -O2 -o readdir_probe readdir_probe.c
./vm-regression.sh
vm-regression.sh performs kldload, creates each vnode-backed md device,
mounts it with mount -t erofs, executes the cold lookup and readdir probes,
unmounts and detaches each image, and finishes with exact module unload.
FreeBSD VM Results
kldload: PASS.- Cold direct read of
/alpha/bravo/charlie/payload.txtwithout parent lookup orreaddir: PASS. - Repeated lookup and sibling nested lookup: PASS.
- Two negative lookups followed by an existing nested lookup: PASS.
- Multi-block
widereaddir: 322 dirents (.,.., 320 files), PASS. - Resume from every one of the 322 returned
d_offcookies: PASS. dot_omittedroot cookies:12,24,47,48; resume at47returns only., and resume at48returns EOF: PASS.- Short directory block: two lookups and direct
getdirentriesall returnEINTEGRITY, PASS. - Non-monotonic
nameoff: two lookups and directgetdirentriesall returnEINTEGRITY, PASS. - Nonzero data after NUL padding: two lookups and direct
getdirentriesall returnEINTEGRITY, PASS. kldunload: PASS.- Post-test EROFS module, mount, and md-device state: clean.
- Final explicit state check: zero EROFS modules, zero EROFS mounts, zero md devices, and no recent panic or fatal trap in dmesg.
Remaining Scope
No unresolved issue remains for the requested cold lookup and directory
regression. The NFS-specific a_cookies consumer path was not exercised by an
NFS export; the tested d_off restart-cookie sequence uses the same generated
cookie values.