# DF-0234 — Signed-integer overflow in callout timer calc (`min_period * hz / 2`)

**Verdict: REPRODUCED (source-level) / LATENT on this guest.**
**Impact: DoS** — CPU-burn (self-perpetuating callout) or watchdog-defeat, **not** memory
corruption; no escalation chain.

## The bug

`sys/kern/kern_wdog.c`, `wdog_reset_all()`:

```c
84: static int
85: wdog_reset(struct watchdog *wd)
86: {
87:     return (wd->period = wd->wdog_fn(wd->arg, wd->period));
88: }
90: static void
91: wdog_reset_all(void *unused)
92: {
93:     struct watchdog *wd;
94:     int period, min_period = INT_MAX;       /* <-- starts at INT_MAX */
...
99:     LIST_FOREACH(wd, &wdoglist, link) {
100:         period = wdog_reset(wd);            /* driver-returned period */
101:         if (period < min_period)
102:             min_period = period;
103:     }
104:     if (wdog_auto_enable) {
105:         callout_reset(&wdog_callout, min_period * hz / 2,   /* <-- SIGNED overflow */
106:                       wdog_reset_all, NULL);
107:     }
```

`min_period` is an `int` initialised to `INT_MAX` and may be set to a driver-returned
`wd->wdog_fn(...)` value (also `int`, `wd->period`). The expression `min_period * hz` is
computed in `int`. With `hz == 1000`, any `min_period > INT_MAX/hz` (≈ 2 147 483) makes
`min_period * hz` overflow signed `int` → negative/garbage. `callout_reset()` then gets a
bogus tick count: a small/zero/negative value arms the callout immediately → the self-
rescheduling `wdog_reset_all` callback burns CPU; a large wrapped value can fail to re-arm the
watchdog → hardware reset. The finding's `INT_MAX`-init path is the obvious trigger; a driver
bug returning a huge period is the other.

## Reachability on this guest (LATENT)

- The only in-tree `wdog_register()` callers are hardware watchdog drivers
  **`amdsbwd.c:519`** (AMD SB7xx/SB8xx watchdog) and **`ichwd.c:593`** (Intel ICH watchdog).
  Neither device is present in this QEMU/KVM guest — `dmesg` shows no `wdog: Watchdog ... registered`
  line, only the subsystem init message. So `wdog_reset_all()` returns early at
  `if (LIST_EMPTY(&wdoglist))` (line 97) on every invocation and the overflow arithmetic is
  never reached.
- `kern.watchdog.period` is root-writable, but `wdog_set_period()`+`wdog_reset_all()` also hit
  the empty-list early return, so the sysctl cannot trigger the multiply either.
- **Conclusion:** the signed-overflow arithmetic is real and confirmed by trace, but it is
  **latent** on this guest — it requires a registered hardware watchdog (amdsbwd/ichwd on real
  hardware) that returns/can be coaxed toward an `INT_MAX`-class period. This is a confirmed
  source-level bug, not a false positive; runtime triggering needs the HW driver.

## The fix

`fix.diff` clamps `min_period` to `INT_MAX / hz` before the multiply so `min_period * hz` can
never overflow a signed `int`. (The companion DF-0236 fix restructures the same function to
release the spinlock around callbacks.)

## Kernel refs
- `sys/kern/kern_wdog.c:94` — `min_period = INT_MAX`
- `sys/kern/kern_wdog.c:105` — `min_period * hz / 2` signed overflow
- `sys/dev/misc/amdsbwd/amdsbwd.c:519`, `sys/dev/misc/ichwd/ichwd.c:593` — only `wdog_register` callers
