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

Divide-by-zero kernel panic when ns_per_byte==0: CBQ class add/modify unconditionally divides by user-supplied value

Summary

rmc_newclass(:237): cl->allotment_=RM_NS_PER_SEC/nsecPerByte. rmc_modclass(:350): same divide. nsecPerByte passed verbatim from user struct cbq_opts.ns_per_byte(u_int) through cbq_add_queue_locked(altq_cbq.c:367,374) with NO zero check anywhere. Single pfaltq config ns_per_byte=0 -> integer divide-by-zero -> kernel panic netisr cpu0. Privileged local DoS. Fix: validate nsecPerByte!=0 at entry.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0441 Β· 9 files
FileTypeDescriptionSize
cbq_div0.c trigger-source DIOCADDALTQ with cbq_opts.ns_per_byte=0 -> div0 5.8 KB view raw
build.sh build-script cc -O2 -Wall -o cbq_div0 cbq_div0.c 89 B view raw
run.sh run-script kldload pf.ko; ./cbq_div0 672 B view raw
VERDICT.md verdict panic reproduction + fix validation 2.7 KB ↓ raw
fix.diff suggested-fix clamp nsecPerByte to 1 in rmc_newclass and rmc_modclass 1.2 KB view raw
panic.txt panic-signature Fatal trap 18 divl %r9d,%eax at rmc_newclass+0x10a 577 B view raw
env.txt environment uname, pf.ko loaded, securelevel 829 B view raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
VERDICT.md verdict panic reproduction + fix validation
↓ download raw

DF-0441 β€” CBQ rmc_newclass / rmc_modclass divide-by-zero

Verdict: REPRODUCED (panic / privileged local DoS).

Mechanism

User-controlled struct pf_altq.pq_u.cbq_opts.ns_per_byte (a u_int, no zero check) flows verbatim from a DIOCADDALTQ/DIOCCHANGEALTQ ioctl through:

altq_cbq.c:374 rmc_newclass(..., opts->ns_per_byte, ...) altq_rmclass.c:237 cl->allotment_ = RM_NS_PER_SEC / nsecPerByte; <-- DIV0

The same pattern is at altq_rmclass.c:350 in rmc_modclass().

pfctl computes ns_per_byte from bandwidth ratios and won't produce 0 (pfctl_altq.c:465), but a malicious root can issue DIOCADDALTQ directly with cbq_opts.ns_per_byte = 0 and trigger the panic.

PoC

cbq_div0.c opens /dev/pf, starts pf + ALTQ, begins an altq transaction (DIOCXBEGIN PF_RULESET_ALTQ), adds a CBQ discipline + a root class on vtnet0 (calls rmc_init, no div), then adds a non-root child class with cbq_opts.ns_per_byte = 0. The third DIOCADDALTQ calls rmc_newclass which panics on the divide.

Trigger requires root: /dev/pf ioctls need root. Privileged local DoS.

Reproduction

On DragonFly 6.5-DEVELOPMENT #0 (unpatched baseline):

# kldload pf.ko
# ./cbq_div0
altq ticket=1
CBQ discipline added; altq_disc=0xfffff80118e63600
Root class added; qid=1

*** ABOUT TO ADD CHILD CLASS WITH ns_per_byte=0 -- EXPECT DIV0 PANIC ***

Panic signature (from dfbsd-qemu/boot.log, see panic.txt):

Fatal trap 18: integer divide fault while in kernel mode
cpuid = 4; lapic id = 4
instruction pointer = 0x8:0xffffffff8072611a
kernel: type 18 trap, code=0
Stopped at  rmc_newclass+0x10a: divl %r9d,%eax

The IP rmc_newclass+0x10a matches the divide at altq_rmclass.c:237.

Fix validation

Applied fix.diff (clamps nsecPerByte to 1 if zero in both rmc_newclass and rmc_modclass), rebuilt the kernel, rebooted into 6.5-DEVELOPMENT #1 (2026-07-18):

# ./cbq_div0
altq ticket=1
CBQ discipline added; altq_disc=0xfffff80118592d00
Root class added; qid=1
*** ABOUT TO ADD CHILD CLASS WITH ns_per_byte=0 -- EXPECT DIV0 PANIC ***
UNEXPECTED: child class added without panic (qid=2)
RUN_EXIT=0

The previously-panicking child-class add now succeeds (nsecPerByte clamped to 1 by the fix). No panic, no trap. Fix is VALIDATED.

Notes

The fix clamps to 1 ns/byte instead of returning EINVAL, because the CBQ config path is several layers deep and returning an error mid-transaction would require unwinding the altq state. Clamping is the cheapest correct behavior: allotment_ becomes RM_NS_PER_SEC (1 byte/sec in some unit interpretation), which is a safe degenerate value.

Fix verification

fixed

validated

see evidence pack
↓ fix.diffDragonFly 6.5-DEVELOPMENT #1: Sat Jul 18 12:48:52 UTC 2026

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

REPRODUCED (live panic). CBQ ns_per_byte=0 -> div-by-zero at rmc_newclass. Root-only /dev/pf.