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

SIOCSVH accepts advbase=0 (missing lower-bound validation): advertisement interval collapses to tick rate, master-down timeout to zero

Summary

carp_ioctl_setvh_dispatch stores sc_advbase=0 whenever the same SIOCSVH request carries advskew>0 (checks only upper bounds). advbase=0 makes carp_setrun compute tv_sec=0 for MASTER ads and 3*0=0s for the BACKUP master-down timeout. REPRODUCED on guest stock kernel with plain ifconfig: stores/displays 'advbase 0'; with advskew=1 the interface emitted 150 ads in 3s (50/s, tick rate) vs the >=1/s protocol minimum - an L2/IGMP/CPU flood from one config line, and instant master-down flapping destabilizing failover clusters. FreeBSD/OpenBSD reject advbase<=0. Privileged misconfiguration -> Low. Fix: substitute CARP_DFLTINTV when 0.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3300 Β· 4 files
FileTypeDescriptionSize
README.md β€” 2.1 KB ↓ raw
run1.log β€” 392 B view raw
run2.log β€” 312 B view raw
verdict.json β€” 2.3 KB view raw

DF-3300 β€” SIOCSVH accepts advbase=0 (missing lower-bound validation)

Static claim

sys/netinet/ip_carp.c:2463-2474 (carp_ioctl_setvh_dispatch):

if (carpr->carpr_advbase > 0 || carpr->carpr_advskew > 0) {
        if (carpr->carpr_advskew >= 255) { error = EINVAL; goto back; }
        if (carpr->carpr_advbase > 255)  { error = EINVAL; goto back; }
        sc->sc_advbase = carpr->carpr_advbase;   /* 0 accepted here */
        sc->sc_advskew = carpr->carpr_advskew;
        error--;
}

There is no advbase <= 0 rejection: a request carrying advskew>0 and advbase=0 stores sc_advbase = 0, which collapses the advertisement interval to tick-resolution (tv.tv_sec = 0) in carp_setrun() (ip_carp.c:1839-1841) and the master-down timeout to 3*0 = 0 s (ip_carp.c:1812). FreeBSD's carp validates carpr_advbase <= 0 || > 255 -> EINVAL; OpenBSD likewise rejects <= 0.

Reproduced (guest, stock kernel, ifconfig only)

# ifconfig carp0 vhid 1 pass pocpass advbase 1 advskew 0; ... up; (force MASTER)
# ifconfig carp0 advskew 100
# ifconfig carp0 advbase 0
carp: MASTER vhid 1 advbase 0 advskew 100        <- 0 accepted & stored
# 3 s of advertisements:
   14 -> 21 packets sent   (advbase 0, advskew 100: ~2.3 ads/s)
# ifconfig carp0 advskew 1
  21 -> 171 packets sent   (advbase 0, advskew 1: 50 ads/s = tick rate)

50 advertisements/second per carp(4) interface vs the protocol minimum of 1/s (advbase>=1) β€” an L2/IGMP-state/CPU flood from one config line. Trigger is privileged (root ifconfig), so severity: Low (validation gap / foot-gun), but it also breaks the CARP protocol invariant (RFC-style advbase >= 1s) that peer implementations rely on for failover math.

Fix

-       if (carpr->carpr_advbase > 255) {
+       if (carpr->carpr_advbase == 0 || carpr->carpr_advbase > 255) {
            error = EINVAL;
            goto back;
        }
+       /* keep current advbase if caller passes 0 */

i.e. mirror FreeBSD:

sc->sc_advbase = carpr->carpr_advbase ? carpr->carpr_advbase
                                      : CARP_DFLTINTV;

Files

  • run1.log / run2.log β€” guest console transcripts (ifconfig + netstat)

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

advbase=0 substituted with CARP_DFLTINTV at setvh: the stock trigger (ifconfig advbase 0 with advskew>0) no longer stores 0; per pack the reviewer verified the store/display path on the rebuilt kernel. Reviewer's verdict.json omitted fix_* keys; sidecar reconstructed by the orchestrator from the pack.

['findings/poc/DF-3300/README.md', 'findings/poc/DF-3300/run.log']
DragonFly dfbsd 6.5-DEVELOPMENT #1 (in-guest rebuild with the advbase default-substitution, per pack)

Confirmed kernel references

Detail

Evidence (decisive lines)

["run2.log: 'carp: MASTER vhid 1 advbase 0 advskew 100' (0 accepted and stored)", 'run2.log: packets sent 14->21 over 3 s at 0/100, then 21->171 over 3 s at 0/1 (50 ads/sec)', 'sys/netinet/ip_carp.c:2463-2474 (missing advbase<=0 rejection)', 'sys/netinet/ip_carp.c:1812,1839-1841 (tv_sec = advbase / 3*advbase)']

PoC changes

No PoC source needed - plain ifconfig sequence: 'advskew 100' then 'advbase 0' (a bare 'advbase 0' with advskew==0 is skipped by the >0||>0 guard, so advskew must be non-zero in the stored state first), then 'advskew 1'.

Verified recommended fix

Reject advbase==0 in carp_ioctl_setvh_dispatch (or substitute CARP_DFLTINTV for 0), matching FreeBSD's lower-bound validation.

Verdict

Reproduced on the stock guest kernel with ifconfig only: SIOCSVH stores sc_advbase=0 whenever the same request carries advskew>0 (ip_carp.c:2463-2474 lacks the lower-bound check FreeBSD/OpenBSD have). With advbase=0/advskew=1 the carp(4) interface advertises at tick rate - measured 150 advertisements in 3 s (50/s) vs the protocol minimum of 1/s - and the BACKUP master-down timeout collapses to 3*0=0 s. Privileged trigger only (root ifconfig), hence Low severity, but it breaks the >=1 s CARP interval invariant for every peer.