# DF-2944 — Uninitialized kernel-stack bytes leaked into process accounting records

**File:** `sys/kern/kern_acct.c` — `acct_process()` (lines 195–282)
**Class:** CWE-457 (use of uninitialized memory) → kernel-stack contents disclosure
**Severity:** Low (acct(2) is root-only; exposure requires a readable accounting file)
**Verdict:** REPRODUCED (3/3 runs) — `LEAK-CONFIRMED`

## What is wrong

`struct acct` (`sys/sys/acct.h:53`) on x86_64 is 56 bytes and contains two
runs of *alignment padding that no field covers*:

| offset | size | content |
|--------|------|---------|
| 22–23  | 2    | pad before `ac_btime` (time_t needs 8-byte alignment) |
| 49–55  | 7    | tail pad after `ac_flag` (struct alignment 8 ⇒ sizeof 56) |

`acct_process()` declares `struct acct acct;` on its kernel stack
(kern_acct.c:198), assigns every *field*, and then writes the full 56 bytes
to the accounting file with `vn_rdwr(...)` (kern_acct.c:276–278). The 9
padding bytes are never initialized, so each record written at every process
exit contains residue of the exiting thread's kernel stack — i.e. fragments
of whatever earlier syscalls left at those stack addresses (in the decisive
run: `78 78 18 01 f8 ff ff` — a pointer-shaped qword `0xfffff80118787800`
in DragonFly's KVA range; across records mostly `ff ff` with variance).

## Reproduce (in-guest, as root)

```
# push acct_leak.c + acct_helper.c to /root/poc/ (e.g. b64decode -r)
cc -O -o /tmp/acct_leak   /root/poc/acct_leak.c
cc -O -o /tmp/acct_helper /root/poc/acct_helper.c
rm -f /var/acct_leak.dat
/tmp/acct_leak
```

The harness: enables accounting on `/var/acct_leak.dat` via `syscall(51,
path)`; forks 25 children that setuid(65534), churn the kernel stack
(30× udp `sendto` to 127.0.0.1:0x5A5A + `getsockname`), and exit; then 25
children `execve("/tmp/acct_helper")` which do the same; disables
accounting; parses all records and hexdumps bytes [22..24) and [49..56).

## Expected output

```
=== 50 records, 50 with nonzero padding (pad1: 49, pad2: 1) ===
LEAK-CONFIRMED
```

with e.g. record 48 showing `pad[49:56] = 78 78 18 01 f8 ff ff`.
Full logs: `build.log`, `run.log`, `run.2.log` (3 runs), raw bytes in
`leak_sample.txt` (hexdump of the accounting file itself).

## Success criterion

≥1 record with non-zero padding bytes that (a) are not written by any
field assignment in kern_acct.c and (b) vary across records/runs —
demonstrating uninitialized kernel-stack disclosure into the file.
Observed: 49–50/50 records per run, 3/3 runs.

## Fix

`bzero(&acct, sizeof(acct))` before filling the record — see `fix.diff`.
