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)
PoC verification
Evidence pack
findings/poc/DF-5569 Β· 3 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | file | 2.8 KB | β raw | |
| fix.diff | file | 387 B | view raw | |
| verdict.json | file | 2.0 KB | view 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_testableNot 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
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).
No comments yet.