# DF-0622 — VERDICT

**Verdict: NOT REPRODUCED — bug pattern is real, but the trigger path is DEAD CODE on this kernel.**

## Mechanism (the bug pattern IS real in source)

`sys/netgraph7/ng_car.c` is a FreeBSD-derived netgraph node type for
Committed Access Rate shaping. It is correctly identified by the finding
as having a NULL-pointer-deref panic window:

- `ng_car_shutdown` (`ng_car.c:539-540`) cancels both pending callouts:
  ```c
  ng_uncallout(&priv->upper.q_callout, node);
  ng_uncallout(&priv->lower.q_callout, node);
  ```
- `ng_car_disconnect` (`ng_car.c:553-580`) does **NOT** — it only purges
  the queue (each `NG_FREE_M` NULLs the mbuf slot at `:563`) and clears
  hook refs (`:569-573`).
- The netgraph callout framework takes its own references on the node and
  hook (`sys/netgraph7/netgraph/ng_base.c:3257-3262`, `ng_callout`):
  ```c
  NG_NODE_REF(node);  NGI_SET_NODE(item, node);
  if (hook) { NG_HOOK_REF(hook); NGI_SET_HOOK(item, hook); }
  ```
  so a pending callout item survives disconnect and later fires.
- When the surviving callout fires `ng_car_q_event` (`ng_car.c:665-707`),
  its drain loop is gated only on `hinfo->tc >= 0` (`:676`) with **no**
  empty-queue guard. After disconnect purged the queue, the loop reads
  ```c
  679:  m = hinfo->q[hinfo->q_first];                  // NULL
  695:  m = hinfo->q[hinfo->q_first];                  // another NULL
  699:  hinfo->tc -= m->m_pkthdr.len;                  // NULL DEREF -> panic
  ```
  → `Fatal trap 12: page fault while in kernel mode` at the `movz` off the
  NULL mbuf.

This bug-pattern analysis is correct.

## Why it CANNOT be triggered on this kernel

`sys/netgraph7/ng_car.c` does **not compile** against the DragonFlyBSD
master kernel. It references three FreeBSD types/functions that do not
exist anywhere in `sys/`:

| Reference in ng_car.c | Exists in DragonFly `sys/`? |
|-----------------------|-----------------------------|
| `struct bintime` (line 62, 588) | **NO** — `grep -rln "struct bintime" sys/` returns ONLY `sys/netgraph7/ng_car.c` |
| `getbinuptime()` (line 210, 592) | **NO** — DragonFly has `getnanouptime(struct timespec *)` instead |
| `bintime_sub()` (line 596) | **NO** — no `bintime` arithmetic in this kernel |

Consequences:

1. `sys/netgraph7/Makefile` SUBDIR list does **NOT** include `ng_car` —
   so `make` in the netgraph7 build tree never builds it.
2. The audit guest's `/boot/kernel/` contains **no `ng_car.ko`**.
3. `kldload ng_car` returns `module not found`.
4. Attempting to build it standalone (from a copied Makefile) fails at
   compile time:
   ```
   ng_car.c:596:2: error: implicit declaration of function 'bintime_sub'
   ng_car.c:588:17: error: unused variable 'newt'
   ```

`ng_car.c` is therefore **dead code** on this kernel. The runtime trigger
path (`ng_car_disconnect` reachable via netgraph `NGM_RMHOOK`) is
**unreachable**. The valid hard blocker applies: *the vulnerable code
path is dead/unreachable at runtime on this guest AND no harness can
exercise it without first porting ng_car.c to DragonFly*.

## PoC

The PoC directory contains:

- `args_overflow.c` — a C stub that documents the bug and explains the
  dead-code situation. (Originally a placeholder name from the empty
  PoC scaffold; renamed-in-place would have been nicer but the
  directory contents are clear.)
- `ng_car_trigger.sh` — the ngctl sequence that would trigger the panic
  on a kernel with a working ng_car module. It documents the trigger
  logic for whenever the module is ported.
- `fix.diff` — git-apply-able fix that adds `ng_uncallout` to
  `ng_car_disconnect` (mirroring shutdown) and adds an empty-queue guard
  to `ng_car_q_event` (defense in depth).

The PoC does NOT trigger a panic on this guest because the module cannot
be loaded. Honest classification: `not_reproduced` due to dead code.

## Recommended fix

`fix.diff` makes two coordinated changes in `sys/netgraph7/ng_car.c`,
both of which are valid regardless of whether the module ever compiles
upstream:

1. In `ng_car_disconnect`, call `ng_uncallout(&hinfo->q_callout, node)`
   before purging the queue — exactly mirroring the cleanup done in
   `ng_car_shutdown`.
2. In `ng_car_q_event`, add an empty-queue guard
   (`if (hinfo->q_first == hinfo->q_last) return;`) right after the
   refillhook call, so the drain loop is not entered on a queue that
   was purged out-of-band.

These are defense-in-depth. **Either change alone closes the panic
window; both together are belt-and-braces.**

This **matches** the finding markdown's proposal (which suggested the
same two changes).

## Caveats

The netgraph7 Car module being unported dead code is itself an
observation worth flagging upstream: either the file should be ported to
DragonFly's time API (and the build Makefile updated to include it), or
it should be removed. As-is, it is a footgun for anyone who tries to
build it.
