DF-0807 / run.log
+ ./harness
=== DF-0807 dirfs_readdir premature-dp-advancement harness ===
Transcription of sys/vfs/dirfs/dirfs_vnops.c:1277-1286
Bug: loop body executes `dp = dpn` BEFORE the for-increment
`bytes -= _DIRENT_DIRSIZ(dp)`, so after the last valid
entry the increment derefs dp->d_namlen past the buffer
(OOB read) and the next iteration copies OOB bytes to the
user readdir sink via vop_write_dirent (info leak).
Scenario: bufsiz=512 name_len=8 (16 entries)
[guard-page variant] bufsiz=512 name_len=8
filled bytes=512 (== bufsiz ? YES โ dp advances PAST allocation)
BUGGY loop: FAULT -> OOB READ CONFIRMED in for-increment `bytes -= DIRSIZ(dp)` (entries=16 bytes_after=32)
FIXED loop: no fault (loop terminated cleanly) (entries=16 bytes_after=0)
[leak-zone variant] bufsiz=512 name_len=8
filled bytes=512 (== bufsiz ? YES), leak zone @ 0x8004a3000, fake d_namlen=7 (DIRSIZ=24 < rec=32)
BUGGY loop: entries=27 bytes_after=-43944 ; sink has OOB marker ? YES -> INFO LEAK CONFIRMED
leaked marker at sink offset 528: 'HEAP-LE'
FIXED loop: entries=16 bytes_after=0 ; sink has OOB marker ? no (clean termination)
Scenario: bufsiz=1024 name_len=8 (32 entries)
[guard-page variant] bufsiz=1024 name_len=8
filled bytes=1024 (== bufsiz ? YES โ dp advances PAST allocation)
BUGGY loop: FAULT -> OOB READ CONFIRMED in for-increment `bytes -= DIRSIZ(dp)` (entries=32 bytes_after=32)
FIXED loop: no fault (loop terminated cleanly) (entries=32 bytes_after=0)
[leak-zone variant] bufsiz=1024 name_len=8
filled bytes=1024 (== bufsiz ? YES), leak zone @ 0x8004ab000, fake d_namlen=7 (DIRSIZ=24 < rec=32)
BUGGY loop: entries=43 bytes_after=-43944 ; sink has OOB marker ? YES -> INFO LEAK CONFIRMED
leaked marker at sink offset 1040: 'HEAP-LE'
FIXED loop: entries=32 bytes_after=0 ; sink has OOB marker ? no (clean termination)
Scenario: bufsiz=2048 name_len=8 (64 entries)
[guard-page variant] bufsiz=2048 name_len=8
filled bytes=2048 (== bufsiz ? YES โ dp advances PAST allocation)
BUGGY loop: FAULT -> OOB READ CONFIRMED in for-increment `bytes -= DIRSIZ(dp)` (entries=64 bytes_after=32)
FIXED loop: no fault (loop terminated cleanly) (entries=64 bytes_after=0)
[leak-zone variant] bufsiz=2048 name_len=8
filled bytes=2048 (== bufsiz ? YES), leak zone @ 0x8004b3000, fake d_namlen=7 (DIRSIZ=24 < rec=32)
BUGGY loop: entries=75 bytes_after=-43944 ; sink has OOB marker ? YES -> INFO LEAK CONFIRMED
leaked marker at sink offset 2064: 'HEAP-LE'
FIXED loop: entries=64 bytes_after=0 ; sink has OOB marker ? no (clean termination)
Scenario: bufsiz=4096 name_len=8 (128 entries, dirfs clamp)
[guard-page variant] bufsiz=4096 name_len=8
filled bytes=4096 (== bufsiz ? YES โ dp advances PAST allocation)
BUGGY loop: FAULT -> OOB READ CONFIRMED in for-increment `bytes -= DIRSIZ(dp)` (entries=128 bytes_after=32)
FIXED loop: no fault (loop terminated cleanly) (entries=128 bytes_after=0)
[leak-zone variant] bufsiz=4096 name_len=8
filled bytes=4096 (== bufsiz ? YES), leak zone @ 0x8004bb000, fake d_namlen=7 (DIRSIZ=24 < rec=32)
BUGGY loop: entries=139 bytes_after=-43944 ; sink has OOB marker ? YES -> INFO LEAK CONFIRMED
leaked marker at sink offset 4112: 'HEAP-LE'
FIXED loop: entries=128 bytes_after=0 ; sink has OOB marker ? no (clean termination)
=== SUMMARY ===
Vulnerable dirfs_readdir loop:
- HEAP OOB READ in the for-increment after the last valid entry
(reads d_namlen from the slab chunk / redzone adjacent to buf)
- KERNEL HEAP INFO LEAK via the next iteration's vop_write_dirent
(bcopy of attacker-recognizable d_ino/d_type/d_namlen/d_name
from past the allocation into the user readdir result)
Fixed loop (remove `dp = dpn` from body):
- bytes correctly decrements by the size of the just-processed entry
- loop terminates with bytes == 0 after the last valid entry
- no OOB read, no leak
Reachability: vkernel64-only (sys/platform/vkernel64/conf/files)
Realistic ceiling: vkernel heap info leak (read-only primitive)
RUN_EXIT=0