# DF-3147 — cd9660_readdir returns uninitialized `idp->uio_off` as the directory offset

## What (root cause)

`cd9660_readdir()` (sys/vfs/isofs/cd9660/cd9660_vnops.c:421-609) allocates

```c
idp = kmalloc(sizeof(*idp), M_TEMP, M_WAITOK);      /* :447  no M_ZERO   */
...
idp->curroff = uio->uio_offset;                     /* :473              */
...
uio->uio_offset = idp->uio_off;                     /* :601  read        */
```

`idp->uio_off` (declared :332) is **only** ever assigned inside
`iso_uiodir()` (:362), i.e. when at least one directory entry is actually
shipped to userspace. If the walk ends without shipping anything —
an 8-byte `getdents` buffer (smaller than `_DIRENT_DIRSIZ` of the first
entry, so `iso_uiodir` returns -1 at :345-348), an empty directory, or a
directory whose entries are all filtered out — `idp->uio_off` is
**uninitialized kmalloc content**, and it is stored into `uio->uio_offset`.

`kern_getdirentries()` (sys/kern/vfs_syscalls.c:4644-4646) then does
`fp->f_offset = auio.uio_offset`, and the value is disclosed to any
unprivileged user via `lseek(fd, 0, SEEK_CUR)` — 8 bytes of stale kernel
heap per call, on **any** mounted cd9660 filesystem (no crafted image
needed), repeatable at will.

The leaked value is the previous tenant of the recycled M_TEMP chunk —
typically a prior `struct isoreaddir` (kernel-internal directory offsets),
but whatever else the allocator hands out for that size class in general.

## Reproduce (guest, stock kernel)

```
vnconfig -c -S 2048 /dev/vn0 /root/df3146_none.iso   # any plain ISO works
mount_cd9660 /dev/vn0 /mnt/a
vnconfig -c -S 2048 /dev/vn1 /root/df3147_big.iso    # second image, dir ends at 1670
mount_cd9660 /dev/vn1 /mnt/b
su -m nobody -c "/tmp/trigger dirent   /mnt/b"       # full read of /mnt/b (uio_off=1670)
su -m nobody -c "/tmp/trigger tinyseek /mnt/a"       # 8-byte getdents on /mnt/a
```

## Expected (observed 2026-09-07)

```
getdents(8) rc=0 ; lseek(SEEK_CUR) = 0x686 (1670) errno=0
```

`/mnt/a`'s own walk would end at offset 338 — the returned 1670 is the
stale `uio_off` of the **unrelated** `isoreaddir` used for `/mnt/b` on the
other filesystem: cross-object kernel-heap disclosure through the file
offset. Reproduced in fresh processes, repeatedly (run.log).

## Impact

Low-severity kernel heap info leak (8 stale bytes per call, unprivileged,
any cd9660 mount) + polluted `f_offset` semantics (subsequent getdents
continue from the garbage offset). Same `isoreaddir` allocation as DF-0866
(pad bytes) but a different field and a different sink (`uio_offset`/
`f_offset` vs `uiomove` padding).

## Fix

Initialize `idp->uio_off = uio->uio_offset;` next to the `curroff`
initialization (fix.diff). Validated on the patched kernel: tiny getdents
now reports offset 0 until an entry is actually shipped (fix_run.log).
