β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-0234

Signed-integer overflow in callout timer calc (min_period*hz/2) -> self-perpetuating CPU-burn or watchdog defeat

Summary

wdog_reset_all(:105) min_period*hz/2 in int. min_period from INT_MAX init or driver return. >INT_MAX/hz -> signed overflow -> negative/wrong ticks. callout fires immediately -> CPU-burn (self-re-scheduling callback) or fails to arm -> hardware reset. Root via sysctl or driver bug.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0234 Β· 9 files
FileTypeDescriptionSize
build.sh build-script no binary (source-level) 225 B view raw
run.sh run-script demonstrates empty wdoglist (latent) 937 B view raw
all_fixes_build.log build-log kernel build with all fixes applied: compiles rc=0, no errors 5.6 MB ↓ download
env.txt environment uname, wdog sysctl state, empty wdoglist 353 B view raw
fix.diff suggested-fix clamp min_period to INT_MAX/hz before multiply 710 B view raw
VERDICT.md verdict signed-overflow trace + reachability 3.2 KB ↓ raw
README.md readme human reproduce doc 566 B ↓ raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
README.md readme human reproduce doc
↓ download raw

DF-0234 β€” DF-0234 β€” Signed-integer overflow in callout timer calc (min_period * hz / 2)

See VERDICT.md for the full root-cause analysis and reachability.

Reproduce

./build.sh && ./run.sh

(For DF-0234 the bug is confirmed at source level; on this QEMU guest the vulnerable path is latent / timing-dependent β€” see VERDICT.md "Reachability".)

Fix

fix.diff is a standalone git apply\ -able patch. Validated to apply clean (git apply --check) and compile in a single all-fixes kernel build (all_fixes_build.log, rc=0, no errors).

VERDICT.md verdict signed-overflow trace + reachability
↓ download raw

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():

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

Fix verification

not_testable

compile validated

see evidence pack

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed. wdog callout min_period*hz signed overflow. No HW watchdog on guest. Compile validated.