DragonFlyBSD Kernel Audit
DF-0787 / panic.txt
← back to finding ↓ download raw
=== DF-0787: kernel panic via unbounded attribute walk in ntfs_loadntnode ===

Trigger (as root, on default GENERIC 6.5-DEVELOPMENT #0):
    vnconfig -c vn0 /root/ntfs_loop.img
    mount_ntfs -o ro /dev/vn0 /mnt/ntfs

The mount syscall enters ntfs_mountfs() which VFS_VGET's NTFS_ROOTINO (ino 5),
the second system node loaded at mount time (sys/vfs/ntfs/ntfs_vfsops.c:396).
ntfs_vget -> ntfs_vgetex -> ntfs_loadntnode() runs the buggy walk at
sys/vfs/ntfs/ntfs_subr.c:305-320. Because the crafted ino-5 MFT record has an
attribute whose reclen is 0 (variant 'loop'), fr_attroff past the resident
attribute list (variant 'oob_a'), or reclen larger than the remaining record
space (variant 'oob_r'), the while() at line 310 never terminates: each
iteration calls ntfs_attrtontvattr(ap) which kmalloc's a fresh struct ntvattr
(+ a resident data buffer) and LIST_INSERT_HEAD's it into ip->i_valist. The
per-type slab allocator trips its memory ceiling and panics.

This is the cited bug, on the cited path. Panic signature (identical for all
three image variants — only cpuid differs):

panic: NTFS vattr: malloc limit exceeded
cpuid = 2
Trace beginning at frame 0xfffff8011803de30
_kmalloc() at _kmalloc+0xb09 0xffffffff806578c9
_kmalloc() at _kmalloc+0xb09 0xffffffff806578c9
ntfs_attrtontvattr() at ntfs_attrtontvattr+0x35 0xffffffff82602685
ntfs_loadntnode() at ntfs_loadntnode+0x178 0xffffffff82604278
ntfs_vgetex() at ntfs_vgetex+0x1ec 0xffffffff826010ac
ntfs_vget() at ntfs_vget+0x29 0xffffffff82601219
Debugger("panic")

CPU2 stopping CPUs: 0x0000003b
 stopped
Stopped at      Debugger+0x7c:  movb    $0,0xbdaf09(%rip)
db>

Reachability note (vs sibling DF-0786):
  The DF-0786 lockmgr panic ("lockmgr: locking against itself") fires inside
  ntfs_ntget() during a directory LOOKUP on an already-locked ntnode, i.e.
  AFTER mount completes. DF-0787 fires BEFORE that — during the initial
  mount-time loadntnode of the system MFT records (which use freshly
  lockinit()'d ntnodes). So the lockmgr bug does NOT block this finding.