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

DRIVERSLEEP() divides by its usec argument with no zero guard (latent SIGFPE/vkernel panic; vkernel64-only divergence from pc64 twin)

Summary

1000000/usec with no zero guard: in a vkernel a divide fault is a host SIGFPE -> vkernel process crash. Latent (the pc64-only callers pass nonzero constants and do not attach in VKERNEL64; pc64's twin clock.c:485 is division-free). Also the 1000000/usec >= hz condition is inverted vs the sleep-long/spin-short intent. Guard fix sketched.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5569 Β· 3 files
FileTypeDescriptionSize
README.md file 2.8 KB ↓ raw
fix.diff file 387 B view raw
verdict.json file 2.0 KB view raw
README.md file
↓ download raw

DF-5569 β€” vkernel64 DRIVERSLEEP(0) divides by zero (latent; no in-platform caller today)

What

sys/platform/vkernel64/platform/systimer.c:358-367:

void
DRIVERSLEEP(int usec)
{
        if (mycpu->gd_intr_nesting_level)
        DELAY(usec);
    else if (1000000 / usec >= hz)          /* <-- usec == 0 => SIGFPE */
        tsleep(DRIVERSLEEP, 0, "DELAY", 1000000 / usec / hz + 1);
    else
        DELAY(usec);
}

DRIVERSLEEP(0) (or negative) evaluates 1000000 / usec β†’ integer division by zero. In the vkernel this is a host SIGFPE delivered to the vkernel process β€” i.e., a kernel-mode divide fault routed through exc_segfault/kern_trap (exception.c:223-229,262-272) β†’ vkernel panic. In a bare-metal platform the same math would be a kernel panic.

Reachability today: NONE in this platform (why this is Info)

Callers found in the whole tree (rg DRIVERSLEEP sys/): - sys/dev/powermng/aps/aps.c:200 β€” DRIVERSLEEP(1) - sys/dev/powermng/aps/aps.c:224 β€” DRIVERSLEEP(500) - sys/dev/misc/psm/psm.c:865 β€” DRIVERSLEEP(10000) - definition twin sys/platform/pc64/isa/clock.c:485 (pc64's version has NO division by usec at all β€” it just picks between two DODELAY modes).

None of these drivers (aps(4), psm(4)) attach in a VKERNEL64 kernel, and all current calls pass nonzero constants. The defect is latent: any future vkernel64 driver (or kld) calling DRIVERSLEEP() with a driver-computed delay of 0 turns it into a vkernel panic. Notably the pc64 twin is immune, so the vkernel64 copy diverges from its sibling for no reason.

The interesting semantic quirk: even for usec > 0 the condition 1000000 / usec >= hz inverts the intent β€” for SMALL usec (busy-wait territory) 1000000/usec is large so it tsleeps for 1000000/usec/hz + 1 ticks, and for LARGE usec (where sleeping makes sense) it DELAYs (busy-waits); e.g. DRIVERSLEEP(10000) with hz=20 gives 100 >= 20 true β†’ sleeps ~1 tick, while DRIVERSLEEP(100000) gives 10 >= 20 false β†’ busy-waits 100 ms in a critical section. This looks inverted relative to the obvious intent (sleep for LONG waits, spin for SHORT ones). Kept as a secondary note; only the div-by-zero is asserted as a defect.

Fix

--- a/sys/platform/vkernel64/platform/systimer.c
+++ b/sys/platform/vkernel64/platform/systimer.c
@@ -356,6 +356,8 @@ DELAY(int usec)
 void
 DRIVERSLEEP(int usec)
 {
+   if (usec <= 0)
+       return;
    if (mycpu->gd_intr_nesting_level)
        DELAY(usec);
    else if (1000000 / usec >= hz)

(and optionally fix the inverted comparison while there, matching pc64's division-free structure).

Verification

Not executed: no code path in a VKERNEL64 kernel calls DRIVERSLEEP() at all, so no in-guest trigger exists; the division-by-zero for usec==0 is plain C semantics (fix.diff guards it). Status: untested β€” hardening finding with no demonstrated impact.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

Not validated on the guest: no VKERNEL64 kernel calls DRIVERSLEEP(), so neither baseline nor patched behavior is observable; the guard is trivially correct by inspection.

README.md fix sketch; caller census in README.md
↓ fix.diffper-fix-DF-5569

Confirmed kernel references

Detail

Evidence (decisive lines)

Source-only: systimer.c:363 ('1000000 / usec'), caller census via rg DRIVERSLEEP sys/ (aps.c:200/224, psm.c:865, pc64 clock.c:485), README.md fix sketch.

PoC changes

n/a (not executed)

Verified recommended fix

Guard DRIVERSLEEP against usec <= 0 before dividing (see README.md diff); consider matching pc64's division-free twin.

Verdict

DRIVERSLEEP() at sys/platform/vkernel64/platform/systimer.c:358-367 evaluates 1000000/usec without guarding usec==0, an integer division by zero that would surface as a SIGFPE-driven vkernel panic. Latent only: the sole in-tree callers (aps.c:200,224; psm.c:865) pass nonzero constants and none of those drivers attach in a VKERNEL64 kernel, and pc64's twin (clock.c:485) has no division at all. Filed as Info hardening; no runtime trigger exists in this platform, so no PoC was run (the arithmetic defect itself is plain C semantics).