DF-3281 / verdict.json
{ "finding_id": "DF-3281", "status": "not_reproduced", "reproduced": 0, "impact": "none", "confidence": "likely", "verdict": "Code defect certain, userland exploitation NOT possible. if_addgroup() executes ifg->ifg_refcnt++ (if.c:1183) outside ifgroup_lock while if_delgroup_locked() decrements under LK_EXCLUSIVE (if.c:1231) - asymmetric, lost-update-capable - and decides group creation without exclusive locking (duplicate same-named groups possible, if.c:1169-1177 + if_creategroup if.c:1106-1123). But ifioctl() holds the global ifnet_mtx across the whole ioctl body (if.c:2029-2450), fully serializing all userland SIOCAIFGROUP/SIOCDIFGROUP calls: a 45s two-racer run (~3.8M cycles) produced 0 anomalous returns (run.log). The live race partners are kernel-side only: if_attachs if_addgroup(IFG_ALL) before ifnet_mtx is taken (if.c:558 vs 689), and if_detachs group teardown which runs after ifnet_mtx is released (if.c:991-996) - i.e. SIOCAIFGROUP racing interface destroy/hot-unplug, both privileged. Consequence if it fires: refcount below membership -> premature if_destroygroup kfree with live ifgl/ifgm -> stale walks/double-free. Severity Low. fix.diff restructures if_addgroup to do scan/create/increment/insert atomically under LK_EXCLUSIVE (with if_creategroup_locked helper); it compiled into fix kernel #1 and 3M+ group cycles ran error-free, but with no userland race baseline fix_status=not_testable.", "exploit_chain": "", "evidence": [ "run.log: racers healthy, 0 MARKER lines, ~1.9M add/del cycles per racer in 45s", "VERDICT.md: serialization analysis (ifnet_mtx) + kernel-side race partners with path:line", "fix.diff: atomic if_addgroup under LK_EXCLUSIVE", "../DF-3280/churnFIX.log: 3.0M SIOCAIFGROUP/SIOCDIFGROUP cycles err=0 on the fix kernel" ], "kernel_refs": [ "sys/net/if.c:1183", "sys/net/if.c:1174", "sys/net/if.c:1231", "sys/net/if.c:991", "sys/net/if.c:558", "sys/net/if.c:2029" ], "poc_changes": "v1 (both racers churn the same ifp) wedges the guest on the first benign EEXIST via the known DF-0272 ifnet_mtx leak; v2 uses one racer per interface (lo0/vtnet0) so benign EEXIST/ENOENT are impossible and any error is a genuine desynchronization marker; group name must not end in a digit (if.c:1155).", "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": 60, "guest_dirty": 0, "build_cmd": "cc -O2 -Wall -o /root/addgrp_race /root/addgrp_race.c", "run_cmd": "(root) /root/addgrp_race", "code_hash": "sha256:3d44566848a30c6cf576cb50719ad1942fd1b713a2bb12ba178e96257e4bbdbe", "notes": "Pass-2 depth result: the ifnet_mtx serialization of the group ioctls is itself the reason no userland race exists; the if_attach/if_detach overlap (interface destroy racing group add) remains the plausible production trigger and is what the fix closes. Guest reset clean after run.", "recommended_fix": "Hoist allocations, then perform membership scan, group lookup/creation, ifg_refcnt++ and both TAILQ insertions under a single LK_EXCLUSIVE section (if_creategroup_locked helper); free allocations on the EEXIST path (see fix.diff).", "fix_status": "not_testable", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Thu Sep 10 03:02:42 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff compiled into fix kernel #1 (with DF-3280s fix) and 3.0M+ group add/del cycles ran error-free on it (churnFIX.log), so the restructure is build- and API-validated; the race itself had no userland-reproducible baseline, so behavioral fix validation is not possible (not_testable).", "fix_evidence": "../DF-3280/kbuild.log (BUILD-EXIT=0), ../DF-3280/churnFIX.log (3.0M cycles err=0), fix.diff" } |