# DF-4229 — cyapa: cyapawrite() returns an uninitialized stack `int` on zero-length writes — stale kernel-stack data leaks to userland as the write() result

## Where
`sys/dev/smbus/cyapa/cyapa.c`

- cyapa.c:791 — `int error;` declared, never initialized.
- cyapa.c:802-809 — the copy loop `while ((n = fifo_space(&sc->wfifo)) > 0
  && uio->uio_resid)` is **skipped entirely** when `uio->uio_resid == 0`
  (zero-length write), so `error` is never assigned.
- cyapa.c:815 — `while (fifo_ready(...) && cmd_completed && error == 0)`
  reads the uninitialized variable (when wfifo is non-empty).
- cyapa.c:1054 — `if (error == 0 && (cmd_completed || uio->uio_resid))`
  reads it **unconditionally** on this path.
- cyapa.c:1056 — `return error;` — the stale 32-bit stack slot becomes the
  d_write return value → `dofilewrite` → write(2) result/errno in userland.

## Reachability of the zero-length write (all cited)
- sys/kern/sys_generic.c:506 `dofilewrite()` calls `fo_write()` with no
  `uio_resid == 0` short-circuit;
- sys/kern/vfs_vnops.c `vn_write()` → `VOP_WRITE_FP()`;
- sys/vfs/devfs/devfs_vnops.c:1366 `devfs_write()` → `dev_dwrite()`;
- sys/kern/kern_device.c `dev_dwrite()` → `cyapawrite()`.
So `write(fd, buf, 0)` on an open `/dev/cyapa*` executes the buggy path.

## Impact
The returned int is stale kernel-stack content of the calling thread
(remnants of prior syscalls: pointers, flags, lengths — 32 bits). In
userland it surfaces as write()'s return value / errno. Weak but real
kernel-stack info disclosure, KASLR-relevant when pointer fragments are
returned. Device node is 0600 root:wheel ⇒ attacker must hold an fd
(root/Xorg or an inherited fd) ⇒ Low severity.

## PoC — `model_uninit_error.c` (runs on any UNIX)
Stack-poison harness reproducing cyapawrite()'s exact control flow
(cyapa.c:785-1056) for `write(fd, buf, 0)`:
- 3/3 runs on the audit guest returned the 0xABABABAB poison pattern
  through `return error;` — deterministic proof that the value flows from
  an uninitialized stack slot (run.log).

## Fix
`int error = 0;` at cyapa.c:791 (fix.diff).

## Build / run
```
cc -O0 -Wall -o model_uninit_error model_uninit_error.c
./model_uninit_error
```
(-O0 so `error` occupies a stack slot exactly as in the kernel frame; the
kernel is built -O2 where the uninitialized load is equally present —
`error` is live across the gotos so the compiler materializes it.)
