# DF-2555: NTFS $AttrDef heap buffer overflow

## Vulnerability
The `$AttrDef` translation loop at `sys/vfs/ntfs/ntfs_vfsops.c:458-460` copies
wchar attribute names from the on-disk `$AttrDef` file into the in-memory
`ntvattrdef` structure using an unbounded `do/while` loop:

```c
j = 0;
do {
    ntmp->ntm_ad[i].ad_name[j] = ad.ad_name[j];
} while(ad.ad_name[j++]);
```

The source `ad.ad_name` is `wchar[NTFS_ATTRNAME_MAXLEN]` = `wchar[0x40]` =
`u_int16_t[64]` = **128 bytes** (64 wchars).
The destination `ntm_ad[i].ad_name` is `char[0x40]` = **64 bytes**.

If a crafted NTFS image has an `$AttrDef` entry whose name field has 64+ non-zero
wchars with no NUL terminator within 64 wchars, and the following struct fields
(`ad_type`, `reserved1`, `ad_flag`, `ad_minlen`, `ad_maxlen`) are also non-zero,
the loop continues past the 64-byte destination buffer, overflowing into adjacent
heap memory.

## Build
```sh
python3 gen_image.py evil.ntfs    # generates the crafted image
```

## Run
```sh
# As root on DragonFlyBSD:
kldload ntfs
vnconfig -c vn0 evil.ntfs
mount_ntfs -o ro /dev/vn0 /mnt/   # overflow happens at mount time
```

## Expected behavior
The mount succeeds (the overflow is silent — the slab allocator doesn't detect
small intra-chunk overflows immediately). With an instrumented kernel, the loop
counter `j` reaches **80** (vs the 63-element limit), proving a **16-byte heap
overflow** past the 64-byte `ad_name` buffer.

## Impact
- **Trigger:** root mounts a crafted NTFS image (e.g. from removable media)
- **Primitive:** heap buffer overflow of 8-16+ bytes past a `struct ntvattrdef`
  allocation (72 bytes, slab zone index 8, chunk size 72 bytes)
- **Overflow target:** adjacent slab chunk in the same 72-byte zone
- **Characterization:** verified with instrumented module showing j=80 (max=63)

## Files
- `gen_image.py` — Python script to generate the crafted NTFS image
- `evil.ntfs` — the crafted image with all-nonzero $AttrDef entry
- `safe.ntfs` — control image with normal null-terminated $AttrDef name
- `fix.diff` — git-apply-able fix bounding the loop
