# DF-3038 — crafted FAT32 "root-alias" directory entry: rmdir frees the live root chain; rename corrupts root entry 1

## Where

* `sys/vfs/msdosfs/msdosfs_lookup.c:411-418` — a directory entry whose start
  cluster equals `pmp->pm_rootdirblk` is silently remapped to `MSDOSFSROOT`,
  so the subsequent `deget()` (denode.c:327-347) returns **the VROOT root
  denode** instead of a fresh denode: a subdirectory entry anywhere on the
  volume aliases the mount root.
* `sys/vfs/msdosfs/msdosfs_vnops.c:1469` (`msdosfs_rmdir` → `detrunc`):
  `detrunc()`'s root guard (denode.c:502) is `VROOT && !FAT32(pmp)` — on
  FAT32 the guard does not fire, so rmdir of the alias truncates the **root
  denode** to length 0 and `freeclusterchain()` (denode.c:602) frees the root
  directory's cluster chain of the still-live mount.
* `sys/vfs/msdosfs/msdosfs_vnops.c:1230-1257` (`msdosfs_rename` ".." update):
  `doscheckpath()` fails to recognize the aliased root as an ancestor because
  FAT32 `".."` entries of top-level dirs report cluster 0 (`MSDOSFSROOT`),
  which ends the walk at lookup.c:847 *before* the `scn == source->StartCluster`
  comparison at :843 can match (source's StartCluster is `pm_rootdirblk`, not
  0).  The rename then overwrites entry 1 of the root directory with a ".."
  pointing at the new parent — clobbering a live root entry and creating a
  namespace cycle (the mount root now lives inside its own subtree).

## Preconditions / trigger

Crafted FAT32 image (root directory crafted empty, one subdirectory entry with
`deStartCluster|deHighClust == pm_rootdirblk`), mounted read-write (root or
`vfs.usermount=1` owner).  Trigger: `rmdir("/mnt/S/rootalias")` or
`rename("/mnt/S/rootalias", "/mnt/T/x")`.

## Impact

Filesystem-structure corruption and namespace confusion **confined to the
attacker's own crafted volume** (the freed root clusters are handed to the
allocator and can subsequently be allocated as file data while still being
walked as the root directory).  No kernel memory-safety violation, no
cross-volume effect ⇒ **Low**.  Phase V guest run skipped (Low severity);
the full path is traced above and the intermediate claims
(empty-root `dosdirempty` == 1, `deget` root special-case, `detrunc` FAT32
guard hole, `doscheckpath` 0-vs-rootdirblk blind spot) are line-verified.

## Fix sketch

1. In `lookup`/`deget`, do not alias a non-root directory entry to the root
   denode: when a scanned entry has `scn == pmp->pm_rootdirblk` (and it is not
   the actual root), return the entry as-is or `EBADF` (mirroring the
   `msdosfs_lookup_checker` corruption handling), instead of rewriting `scn`
   to `MSDOSFSROOT`.
2. Make `detrunc()`/`deextend()` reject `VROOT` for **all** FAT types
   (drop `&& !FAT32(pmp)`), matching the read-only root semantics used
   elsewhere.
3. In `doscheckpath()`, compare against both root representations
   (`MSDOSFSROOT` and `pmp->pm_rootdirblk`) before the `scn == MSDOSFSROOT`
   early-exit.
