# DF-5775 — writepid() follows symlinks and truncates the `-p` target with the launcher's privileges

**File:** `sys/platform/vkernel64/platform/init.c`
**Lines:** 1388-1401 (`writepid`)
**Severity:** Low (arbitrary-file truncate/overwrite via symlinked pidfile, confused-deputy against root-launched vkernels)
**Status:** reproduced (stock) / fix validated.

## Reproduce (guest, root)

```
cd /usr/obj/usr/src/sys/VKERNEL64
echo "DF5775-MARKER-SECRET-ORIGINAL-CONTENT-MUST-SURVIVE" > /tmp/df5775_target
ln -sf /tmp/df5775_target /tmp/df5775_link
./kernel.debug -p /tmp/df5775_link        # writepid runs before -m validation
cat /tmp/df5775_target
```

## Expected output

Vulnerable (baseline_run.log):

```
before: target=DF5775-MARKER-SECRET-ORIGINAL-CONTENT-MUST-SURVIVE
after:  target=[14255]                     <- pid number only
RESULT: target CLOBBERED via symlink (vulnerable)
kernel.debug: System memory size must be specified with -m
```

The symlink itself survives (`ls -la`); the TARGET was truncated to 0
(`ftruncate`, init.c:1400) and overwritten with the pid string.

Fixed (fixed_run.log, rebuilt kernel with fix.diff hunk 3):

```
after:  target=[DF5775-MARKER-SECRET-ORIGINAL-CONTENT-MUST-SURVIVE]
RESULT: target intact (not vulnerable)
Failed to create pidfile: Too many links     <- ELOOP from O_NOFOLLOW
```

## Root cause / threat

`open(pid_file, O_RDWR|O_CREAT|O_EXLOCK|O_NONBLOCK, 0666)`
(init.c:1390) has no `O_NOFOLLOW`: a symlinked `-p` path is followed,
then `ftruncate(fd, 0)` + `write(fd, pid)` (init.c:1400-1401) destroy
and replace the target's content. Production vkernels are launched by
root (rc scripts, management wrappers) and the pidfile path is exactly
the kind of field that flows from configuration; a root-launched
vkernel fed `-p /attacker/link -> /etc/anything` truncates that file to
8 bytes of pid. Classic CWE-59 pidfile clobber, in base-system
infrastructure.

Note: writepid() runs before init_sys_memory() (init.c:442-444), so the
clobber needs no successful boot — a vkernel that immediately errors
out still truncates the target.

## Fix

`fix.diff` hunk 3: add `O_NOFOLLOW` (operator who genuinely wants a
symlinked pidfile can still point the link at a dedicated file created
first; the error is loud: "Failed to create pidfile: Too many links").
