Files
erofs-freebsd-out-tree/tests/results/manual/2026-08-08T1800Z-namei/manual-test-report.md
T
2026-08-13 10:44:59 +02:00

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 ENOENT misses.
  • 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 . at i_size for dot_omitted, matching Linux EROFS.
    • Aligns restart positions relative to each directory block and preserves the i_size cookie needed to resume the synthetic dot entry.
    • Initializes returned cookie-array outputs before allocation.

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.ko SHA-256 remained 50a19ea96c7414f73d92049671e66a9eacc9ff5abe27950a46d16c8e6c763baf.
  • nm -u build/erofs.ko | grep -w bcmp: no match.
  • git diff --check for 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.txt without parent lookup or readdir: PASS.
  • Repeated lookup and sibling nested lookup: PASS.
  • Two negative lookups followed by an existing nested lookup: PASS.
  • Multi-block wide readdir: 322 dirents (., .., 320 files), PASS.
  • Resume from every one of the 322 returned d_off cookies: PASS.
  • dot_omitted root cookies: 12, 24, 47, 48; resume at 47 returns only ., and resume at 48 returns EOF: PASS.
  • Short directory block: two lookups and direct getdirentries all return EINTEGRITY, PASS.
  • Non-monotonic nameoff: two lookups and direct getdirentries all return EINTEGRITY, PASS.
  • Nonzero data after NUL padding: two lookups and direct getdirentries all return EINTEGRITY, 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.