DF-5835 / verdict.json
{ "finding_id": "DF-5835", "status": "reproduced", "verdict": "REPRODUCED unprivileged (root and uid=1001): all four tick-conversion routines in kern_clock.c guard only the seconds term with `sec <= INT_MAX / hz` (kern_clock.c:1198/1229/1257/1271) and then add the sub-second term (+1 in the _high variants), so a normalized duration at exactly the boundary (sec=INT_MAX/hz=21474836 at hz=100 with large usec/nsec) computes sec*hz+ceil(usec/ustick)+1 = 2147483701 > INT_MAX \u2014 the (int) cast wraps to -2147483595 (signed-overflow UB). Live: kevent(EVFILT_TIMER, data=21474836999ms) one-shot ~249-day timer fires in 0.012-0.019s; setitimer(ITIMER_REAL,{21474836s,999999us}) delivers SIGALRM in 0.017s; the 1-second-of-ms-lower control (21474835999) does NOT fire within the 2s kevent timeout, pinning the effect to the exact predicted boundary. Impact: none beyond corrupted timer semantics (the negative count is clamped by callout_reset to a 1-tick minimum per kern_timeout.c:788-791/968 \u2014 no memory corruption); kernel-level signed-overflow UB on an unprivileged path.", "reproduced": true, "impact": "none", "confidence": "certain", "exploit_chain": "", "evidence": "run.log: [kevent] boundary data=21474836999ms: fired after 0.012494 s (n=1) + [kevent] control data=21474835999ms: n=0 after 2.010011 s + [setitimer] boundary {21474836s,999999us}: SIGALRM after 0.017077 s + identical unprivileged run (uid=1001, fired 0.015120 s). build.log: BUILD_OK + hz=100 clockrate. VERDICT.md carries the full arithmetic and path:line trace.", "kernel_refs": [ "sys/kern/kern_clock.c:1198", "sys/kern/kern_clock.c:1199", "sys/kern/kern_clock.c:1229", "sys/kern/kern_clock.c:1230", "sys/kern/kern_clock.c:1257", "sys/kern/kern_clock.c:1271", "sys/kern/kern_event.c:480", "sys/kern/kern_event.c:425", "sys/kern/kern_time.c:968", "sys/kern/kern_timeout.c:788" ], "poc_changes": "PoC authored fresh (no seed): kevent + setitimer rigs in one binary with boundary and control cases; comments initially mislabeled the magnitude as ~68 years (21474836999 ms is ~249 days) \u2014 fixed in prose, behavior unchanged; three builds/runs total across root and unprivileged users.", "attempts": 3, "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": 70, "guest_dirty": 0, "build_cmd": "cc -O2 -Wall -o tvtohz_ovf tvtohz_ovf.c", "run_cmd": "./tvtohz_ovf kevent; ./tvtohz_ovf setitimer", "code_hash": "4128bb110a4ff61c158c9ae70ea5f8da3ba3d0172800a8fc6310c1d47d8f1a4d", "notes": "Boundary math is hz-dependent; guest runs at hz=100 where INT_MAX/hz = 21474836 and howmany(999000us,10000us)=100 -> 2147483701 overflow. Other unprivileged consumers of the same family: sigtimedwait path (kern_sig.c:1802 tstohz_high), POSIX mqueue waits (sys_mqueue.c:233 tvtohz_high), bpf timed reads (tvtohz_low). Guest artifacts removed after runs; guest still up.", "recommended_fix": "Tighten the seconds guard in all four converters to (INT_MAX - hz - 1) / hz so sec*hz + sub-second term (+1) can never exceed INT_MAX (see fix.diff).", "fix_status": "not_tested", "fix_baseline_reproduced": true, "fix_patched_reproduced": false, "fix_verdict": "fix.diff authored from line-accurate root cause (guard tightened in all four functions); kernel rebuild + patched retest not performed for this Low-severity functional finding (no memory corruption involved).", "fix_evidence": "findings/poc/DF-5835/fix.diff" } |