DF-5842 / verdict.json
{ "finding_id": "DF-5842", "status": "reproduced", "reproduced": 1, "impact": "dos", "confidence": "certain", "verdict": "Reproduced unprivileged (uid=1001), deterministic across 3 runs: sys_select (sys_generic.c:807-815), sys_pselect (:842-849) and sys_ppoll (:1271-1278) copy the user timeout and pass it to kern_kevent with no range/normalization check. pselect(ppoll){tv_sec:-1} returns rc=0 in ~20-90us (POSIX: EINVAL); select{tv_usec:2^62} returns rc=0 in ~40-73us - a ~146-year intended sleep collapses to immediate return via the (int) truncation in tvtohz_highs tick math (kern_clock.c:1196-1200); ppoll{tv_nsec:2^62} (PRECISE path) returns after ~1.7-2.0s from the int-overflowing ustimeout multiply at kern_event.c:975; select{tv_usec:1500000} is accepted and sleeps 1.5s. The nd==0 path of the same syscalls routes through nanosleep1->clock_nanosleep1 which DOES validate (kern_time.c:467, returns EINVAL) - select(0fds,-1s)=EINVAL while select(fd,-1s)=success proves the missing entry check is the defect. Complementary to DF-5835 (kern_clock.c converters): DF-5835s converter fix does not stop negative/unnormalized values and does not cover kern_event.c:975. Impact: unprivileged timer-semantics corruption (long polls silently become immediate/arbitrary short returns - watchdog/long-poll spin), POSIX violation; no memory corruption.", "exploit_chain": "unprivileged pselect/ppoll/select with negative or unnormalized timeout -> kern_kevent raw timespecadd -> expired-target immediate return or int-truncated tick/us count -> syscall returns success immediately/after arbitrary wrong duration; applications relying on long timeouts spin or misbehave (DoS-class).", "evidence": "run.log + run.2.log: T1 pselect{-1s} rc=0 elapsed=0.000019-0.000087; T2 ppoll{0,2^62ns} rc=0 elapsed=1.694-1.982s; T2b ppoll{-1s} rc=0 elapsed~15us; T3 select{0,-5us} rc=0 elapsed~20us; T7 select{0,1.5e6us} rc=0 sleeps 1.501s; T6 select(0fds){-1s} rc=-1 EINVAL=22 (validating path); T4/T5 controls 2.000s/0.500s. T8 (run.2.log tail) select{0,2^62us} rc=0 elapsed=0.000038-0.000073 across 3 executions.", "kernel_refs": [ "sys/kern/sys_generic.c:807", "sys/kern/sys_generic.c:842", "sys/kern/sys_generic.c:1271", "sys/kern/kern_event.c:872", "sys/kern/kern_event.c:975", "sys/kern/kern_clock.c:1196", "sys/kern/kern_time.c:467" ], "poc_changes": "No seed rig; wrote from scratch. First version had an unbounded T7 (select{0,4e9us}) that legitimately slept 4000s and wedged the ssh session - confirming normalized huge positive values are honored - then bounded every test with alarm(30) and re-scoped T7 to {0,1500000us}; added T8 (t8.c) for the non-precise tstohz_high path.", "attempts": 2, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 8, "guest_dirty": 0, "build_cmd": "cc -O -o /tmp/timeout_rig /tmp/timeout_rig.c && cc -O -o /tmp/t8 /tmp/t8.c", "run_cmd": "/tmp/timeout_rig && /tmp/t8 && /tmp/t8", "code_hash": "1c00b9b343217e4ce5293c0e46e76e7226c26ec65d83cf293b623878a7195634", "notes": "Negative-timeout immediate return is the syscall returning success with 0 ready fds - indistinguishable from a spurious wakeup, so affected daemons spin without any diagnosable error. Severity Low (semantics/DoS-class, consistent with DF-5835s Low); bucket base:*. Guest stayed up, no panic, no dirty state.", "recommended_fix": "Validate the timeout at each syscall entry exactly like clock_nanosleep1: reject tv_sec<0 or tv_nsec<0 or tv_nsec>=1e9 (timeval: tv_usec<0 or >=1e6) with EINVAL in sys_select/sys_pselect/sys_ppoll", "fix_status": "not_tested", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff (3 hunks) adds the clock_nanosleep1-identical validation to all three entry points; with it T1/T2/T2b/T3/T7/T8 become clean EINVAL and the T4/T5 controls are unchanged. Not kernel-rebuilt: Low-severity non-memory-corruption finding, rebuild not mandated by contract; patch is a direct mirror of the already-proven kern_time.c:467 predicate.", "fix_evidence": "fix.diff (unified, against read-only sys/, never applied); baseline behavior in run.log/run.2.log; validating-path contrast T6 proves EINVAL is the kernels existing behavior for the same input when routed through nanosleep1." } |