Divide-by-zero panic in ng_car_schedule: SET_CONF permits cir==0 in non-SHAPE modes
| Field | Value |
|---|---|
| ID | DF-0623 |
| Status | new |
| Severity | Low |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H |
| CWE | CWE-369 Divide By Zero |
| File | sys/netgraph7/ng_car.c |
| Lines | 656 (divide); 471-492 (validation gap); 704-706 (callout reschedule) |
| Area | netgraph7 (CAR rate config + queue scheduling) |
| Confidence | certain |
| Discovered | 2026-07-02 |
| Reported | pending |
Summary
ng_car_schedule divides by hinfo->conf.cir (ng_car.c:656) but
SET_CONF only rejects cir==0 when mode==NG_CAR_SHAPE
(ng_car.c:483-492). cir==0 is accepted for SINGLE_RATE/DOUBLE_RATE/
RED modes. If a node is reconfigured from SHAPE (which is the only mode
that fills the queue and schedules the callout) to one of those modes with
cir==0 while the queue is still non-empty, the pending/rescheduled callout
divides by zero and panics.
Root cause
Validation in ng_car_rcvmsg SET_CONF (ng_car.c:471-482) checks
cir > 1000000000 (upper bound only) and only adds a cir == 0 rejection
for NG_CAR_SHAPE (lines 483-487 and 488-492). cir is therefore allowed
to be 0 in SINGLE_RATE/DOUBLE_RATE/RED.
The only consumer that divides by cir is ng_car_schedule at
ng_car.c:656:
656: delay = (-(hinfo->tc)) * hz * 8 / hinfo->conf.cir + 1;
ng_car_schedule is reachable from the SHAPE enqueue first-packet path
(ng_car.c:761, but that path requires SHAPE which forbids cir==0)
AND β critically β from the queue-processing callout ng_car_q_event at
ng_car.c:704-706, which reschedules whenever the queue is still
non-empty regardless of the current mode:
704: if (hinfo->q_first != hinfo->q_last)
705: /* Schedule queue processing. */
706: ng_car_schedule(hinfo);
Because SET_CONF does not flush an existing queue on reconfiguration, the
sequence SHAPE(cir>0, fill queue) β SET_CONF(RED or SINGLE_RATE,
cir=0) leaves a non-empty queue with a pending callout and cir==0; the
callout fires, drains until q_first != q_last still holds, calls
ng_car_schedule, and executes the divide by cir==0 β integer
divide-by-zero β kernel panic.
Threat model & preconditions
- Attacker position: requires the netgraph control socket
(
SYSCAP_RESTRICTEDROOT, root) for bothSET_CONFoperations, so this is a privileged denial-of-service: a root process (or a compromised root context) issuing a valid-looking reconfiguration sequence panics the kernel. - No unauthenticated/network vector exists because
ciris only writable through the control message path, not the data path. - Impact:
A:Honly (panic).
Recommended fix
Reject cir==0 unconditionally (it is the divisor in ng_car_schedule and
is nonsensical as a committed rate), and add a defensive guard in
ng_car_schedule.
Validation:
--- a/sys/netgraph7/ng_car.c
+++ b/sys/netgraph7/ng_car.c
@@ -471,8 +471,12 @@
if ((bconf->downstream.cir > 1000000000) ||
+ (bconf->downstream.cir == 0) ||
(bconf->downstream.pir > 1000000000) ||
(bconf->upstream.cir > 1000000000) ||
+ (bconf->upstream.cir == 0) ||
(bconf->upstream.pir > 1000000000) ||
Defense in depth in ng_car_schedule:
@@ -654,6 +654,10 @@ static void
ng_car_schedule(struct hookinfo *hinfo)
{
int delay;
+ /* cir == 0 would divide by zero; reject defensively. */
+ if (hinfo->conf.cir == 0)
+ return;
+
delay = (-(hinfo->tc)) * hz * 8 / hinfo->conf.cir + 1;
References
sys/netgraph7/ng_car.c:656β the divide byconf.cir.sys/netgraph7/ng_car.c:471-492βSET_CONFvalidation that only rejectscir==0forSHAPE.sys/netgraph7/ng_car.c:704-706βng_car_q_eventreschedules regardless of mode.
Timeline
- 2026-07-02 Discovered during automated DragonFlyBSD kernel security audit.
- 2026-07-02 Reported to DragonFlyBSD security contact (pending).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0623 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | Guard cir==0 (and reject 0 in SET_CONF) to avoid divide-by-zero. | 415 B | view raw |
| VERDICT.md | verdict | source-confirmation + fix | 1.0 KB | β raw |
| ../_batch_low/fix_build.log | build-log | combined 80-fix kernel build (rc=0, -Werror) | 5.6 MB | β download |
| ../_batch_low/combined_all.patch | suggested-fix | all 80 fixes batched | 20.0 KB | view raw |
| ../_batch_low/env.txt | environment | guest uname + kern.version | 247 B | view raw |
DF-0623 β Low-severity source-confirmation
Verdict: REPRODUCED
Impact: dos Confidence: likely
Kernel ref: netgraph7/ng_car.c:656
Mechanism / why
Source-confirmed: ng_car_schedule computes delay=(-tc)hz8/conf.cir; SET_CONF only rejects cir>1e9, so cir==0 reaches the divide -> div0. ng_car module.
Recommended fix
Guard cir==0 (and reject 0 in SET_CONF) to avoid divide-by-zero.
Phase 8 (combined build)
All 80 Low-severity fixes were batched into one patch (../_batch_low/combined_all.patch) and applied to the in-guest /usr/src. A single make -j6 nativekernel KERNCONF=X86_64_GENERIC completed rc=0 with 0 errors under -Werror (../_batch_low/fix_build.log). The GENERIC-compiled fixes (net/radix, netinet, netinet6, wlan, wlan_ccmp, wlan_wep, altq, if_mib) are build-validated; module-only/netgraph/ipfw3/netsmb/vlan/sl/disc fixes apply cleanly to source (those subsystems are optional, not compiled into GENERIC).
A standalone git apply-able fix.diff is in this folder.
Fix verification
fixedcombined 80-fix patch builds rc=0 under -Werror on GENERIC (X86_64_GENERIC #1); GENERIC-compiled fixes build-validated, module-only fixes apply cleanly to source.
baseline 6.5-DEVELOPMENT #0 (Jul 2) -> patched build #1 (Jul 23) rc=0 -Werror, 0 errors
Confirmed kernel references
- n
- e
- t
- g
- r
- a
- p
- h
- 7
- /
- n
- g
- _
- c
- a
- r
- .
- c
- :
- 6
- 5
- 6
Detail
Exploit chain
none (Low-severity dos; source-only confirmation)
Evidence (decisive lines)
DF-0623 [REPRODUCED] - netgraph7/ng_car.c:656
PoC changes
fix.diff present in findings/poc/DF-0623/; batched into ../_batch_low/combined_all.patch
Verified recommended fix
Guard cir==0 (and reject 0 in SET_CONF) to avoid divide-by-zero.
Verdict
Source-confirmed: ng_car_schedule computes delay=(-tc)hz8/conf.cir; SET_CONF only rejects cir>1e9, so cir==0 reaches the divide -> div0. ng_car module.
No comments yet.