Signed integer overflow in amdsb_watchdog() timeout calc programs watchdog count=0 -> deterministic reboot
Summary
amdsb_watchdog computes hardware tick count as (period*1000)/sc->ms_per_tick in plain int arithmetic. period is arbitrary int from kern.watchdog.period sysctl root-writable no period_max clamp. For period~=4294967 period*1000 overflows INT_MAX wraps to negative divides to 0 hardware count register programmed to 0 while function returns original huge period causing reload callout scheduled ~24 days out. AMD southbridge watchdog fires on near-zero count reboots machine. Deterministic local DoS via arithmetic logic bug. Clamp at :218-219 only caps FINAL value cannot undo overflow. Negative period even simpler. Root-only sysctl gated.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2540 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| build.sh | build-script | no binary to build (HW-gated) | 250 B | view raw |
| run.sh | run-script | HW-gate check + arithmetic summary | 1002 B | view raw |
| df2540_arith.py | trigger-source | verifies signed-overflow produces count=0 for period~4294967 | 1.3 KB | view raw |
| arith_output.txt | run-log | full period -> count table showing count=0 window | 1.8 KB | view raw |
| VERDICT.md | verdict | analysis: overflow math, HW-gating, fix | 4.2 KB | β raw |
| fix.diff | suggested-fix | cast period to int64_t before multiplication | 425 B | view raw |
| fix_build.log | build-log | amdsbwd.ko rebuild with fix (rc=0) | 6.9 KB | view raw |
| env.txt | environment | QEMU chipset, /dev/amdsbwd absent | 458 B | view raw |
DF-2540 β Signed integer overflow in amdsb_watchdog() timeout calculation
Verdict
NOT REPRODUCED on this guest (HW-gated β no AMD SB600/SB7xx southbridge in QEMU).
However, the code bug is CONFIRMED by source tracing and arithmetic verification: the
signed integer overflow at line 217 produces a watchdog count of 0 for specific period
values, bypassing the max-ticks clamp and programming the hardware to fire immediately.
Bug analysis
amdsb_watchdog() (sys/dev/misc/amdsbwd/amdsbwd.c:211-228):
static int
amdsb_watchdog(void *arg, int period)
{
unsigned int timeout;
struct amdsbwd_softc *sc = arg;
timeout = (period * 1000) / sc->ms_per_tick; // line 217: SIGNED overflow
if (timeout > sc->max_ticks) // line 218: clamp β can't undo overflow
timeout = sc->max_ticks;
...
amdsbwd_tmr_set(sc, timeout); // programs hardware count register
return period;
}
period is int (from kern.watchdog.period sysctl). period * 1000 is int Γ int β int.
For period > INT_MAX/1000 = 2147483, this overflows signed int β undefined behavior in C.
On two's-complement hardware (gcc 8.3 default), it wraps.
With sc->ms_per_tick = 1000 (the only value assigned, amdsbwd.c:472):
period |
period*1000 as int32 |
/1000 (C trunc toward 0) |
unsigned int |
> max_ticks? |
HW count |
|---|---|---|---|---|---|
| 2147483 | 2147483000 | 2147483 | 2147483 | yes β 65535 | 65535 (safe) |
| 2147484 | -2147483296 | -2147483 | 4292819813 | yes β 65535 | 65535 (safe) |
| 4294967 | -296 | 0 | 0 | NO | 0 β FIRE! |
| 4294966 | -1296 | -1 | 4294967295 | yes β 65535 | 65535 (safe) |
period = 4294967: 4294967 * 1000 = 4294967000, which as int32 wraps to -296.
Then -296 / 1000 = 0 (C integer division truncates toward zero, and |-296| < 1000).
timeout = (unsigned int)0 = 0. Since 0 <= max_ticks(65535), the clamp does NOT fire.
amdsbwd_tmr_set(sc, 0) programs the AMD SB watchdog count register to 0, causing the
watchdog to fire immediately β deterministic system reboot.
The finding's analysis is correct: the signed overflow produces count=0 for period β 4294967,
bypassing the clamp and causing an immediate reboot on real AMD SB hardware.
Why it can't be reproduced on this guest
The amdsbwd device only attaches when AMD SB600/SB7xx/SB8x hardware is present
(amdsbwd_identify checks pci_find_bsf(0, 20, 0) for AMD SMBus device IDs at
amdsbwd.c:245-251). The QEMU guest uses an Intel 440FX / PIIX3 chipset β no AMD SB
device exists:
$ ls /dev/amdsbwd* β No such file or directory $ pciconf -lv | grep AMD β (nothing β Intel 440FX/PIIX3/PIIX4 only)
Without the device, amdsb_watchdog is never registered as a watchdog_list callback,
so kern.watchdog.period changes never reach the vulnerable code. The bug is latent on
this guest β real on physical AMD SB hardware, unreachable in QEMU.
Impact
- CVSS PR:H β
kern.watchdog.periodrequires root to set. - HW-gated β requires AMD SB600/SB7xx/SB8x southbridge.
- On real hardware: root sets
kern.watchdog.period=4294967β AMD SB watchdog count=0 β immediate reboot. Local DoS via arithmetic logic bug. - Not remotely exploitable; not unprivileged.
Fix
Cast period to int64_t before multiplication to eliminate the signed overflow:
- timeout = (period * 1000) / sc->ms_per_tick;
+ timeout = ((int64_t)period * 1000) / sc->ms_per_tick;
With the fix, period=4294967 β (int64_t)4294967 * 1000 = 4294967000 (fits in int64),
/1000 = 4294967, > max_ticks(65535) β clamped to 65535. Correct.
Validated: amdsbwd.ko rebuilt with the fix (make KERNCONF=X86_64_GENERIC in
sys/dev/misc/amdsbwd/, rc=0). Behavioral testing not possible (no AMD SB hardware in QEMU);
fix correctness is verified by arithmetic.
Fix verification
not_testablenot_testable: amdsbwd.ko rebuilt with int64_t cast fix (rc=0, compiles cleanly) but device cannot attach without AMD SB600/SB7xx hardware (absent in QEMU). Fix correctness verified by arithmetic: ((int64_t)4294967*1000)/1000=4294967 -> clamped to max_ticks(65535), NOT 0. Diff applies and compiles.
fix_build.log: amdsbwd.ko link -> rc=0. Arithmetic: patched timeout=((int64_t)4294967*1000)/1000=4294967 > 65535 -> clamped to 65535 (correct). Unpatched: timeout=(int)4294967*1000/1000 -> -296/1000=0 -> count=0 (bug). Cannot live-test: no AMD SB HW.
Confirmed kernel references
Detail
Exploit chain
none (HW-gated, not memory corruption). On real AMD SB hardware: root sets kern.watchdog.period=4294967 -> amdsb_watchdog computes timeout=0 via signed overflow -> wdcount_write(0) -> AMD SB watchdog fires immediately -> deterministic reboot. Local DoS (PR:H, HW-gated).
Evidence (decisive lines)
QEMU HW-gate: /dev/amdsbwd absent; pciconf shows Intel 440FX/PIIX3/PIIX4 only (no AMD). Arithmetic: period=4294967 -> int32(4294967*1000)=-296 -> -296/1000=0 -> count=0 -> FIRE (count=0 window confirmed for period 4294967-4294968).
PoC changes
Created df2540_arith.py (simulates int32 overflow arithmetic, generates full period->count table), run.sh (HW-gate check + summary), build.sh, VERDICT.md, fix.diff.
Verified recommended fix
Cast period to int64_t before multiplication: timeout=((int64_t)period1000)/sc->ms_per_tick at amdsbwd.c:217. Eliminates signed overflow; period=4294967 -> (int64_t)42949671000/1000=4294967 -> clamped to max_ticks(65535). Correct. Matches finding proposal intent. Full diff in findings/poc/DF-2540/fix.diff.
Verdict
NOT REPRODUCED on this guest (HW-gated). The amdsbwd device requires AMD SB600/SB7xx/SB8x southbridge hardware; the QEMU guest uses Intel 440FX/PIIX3 -- no AMD SMBus PCI device at 0:20:0, so amdsbwd_identify returns without attaching, amdsb_watchdog never registered, /dev/amdsbwd absent. HOWEVER code bug CONFIRMED by source tracing and arithmetic verification: amdsb_watchdog (amdsbwd.c:217) computes timeout=(period1000)/ms_per_tick in int arithmetic. With ms_per_tick=1000 (only value, amdsbwd.c:472) and period=4294967: int32(42949671000)=-296, -296/1000=0 (C truncation toward zero), timeout=0, 0<=max_ticks so NOT clamped -> hardware watchdog count register programmed to 0 -> immediate reboot. Finding's analysis correct.
No comments yet.