# DF-3528 — ng_car SHAPE callout leaks ng_item + node/hook refs (DF-3523 twin)

## What
`sys/netgraph7/ng_car.c` arms its per-hook SHAPE timer with netgraph7's
item-carrying callout API and cancels it with `ng_uncallout()`:

- arm:   `ng_car_schedule()` — ng_car.c:656-660 → `ng_callout(&hinfo->q_callout,
  node, hook, delay, ng_car_q_event, NULL, 0)`
- cancel: `ng_car_shutdown()` — ng_car.c:539-540 → `ng_uncallout(&priv->upper/lower.q_callout, node)`

`ng_callout()` (sys/netgraph7/netgraph/ng_base.c:3248-3269) allocates a fresh
ng_item and takes NG_NODE_REF + NG_HOOK_REF on it every call.
`ng_uncallout()` (ng_base.c:3273-3296) is supposed to free that item on a
successful armed-cancel, but:

1. layer 1 (DF-3515): kern_timeout `_callout_cancel_or_stop` returns 0 for
   inline-completed stops, so on a stock kernel `callout_stop(c)` returns 0 and
   the cleanup at ng_base.c:3284-3292 is never reached;
2. layer 2 (DF-3474/DF-3523): even when reached, `NG_FREE_ITEM(item)` is a
   flag-only macro in netgraph7 (netgraph.h:787-792) — `ng_free_item()` is
   never invoked, so the item + its node reference + its hook reference leak
   on ANY kernel.

Net effect for ng_car: every ng_car node destroyed while a SHAPE timer is
armed leaks 1 ng_item + 1 node ref + 1 hook ref per armed hook (up to 2 per
node). The leaked node ref pins the dead node and its name registration
forever (DF-3523 measured this exact mechanism on ng_pptpgre: 5.76K allocs /
830 KB per 30k cycles, kldunload → "Device busy", and at scale the leaked
node refs drove the DF-3514 idhash-rehash panic).

Rearm note (lead (a) of this audit batch): the arm-then-rearm path
(q_event → ng_car_schedule) only ever re-arms an *idle* callout, because
q_event runs as an NGQF_WRITER item and is serialized against rcvdata by
ng_apply_item's reader/writer protocol (ng_base.c:1993-1999). The orphaned
`oitem` dead-store at ng_base.c:3266 is therefore NOT reachable through
ng_car's flow; the leak is exactly the cancel path. (The disconnect path
never cancels at all — that is known DF-0622 — but ng_car_shutdown's
ng_uncallout still runs at rmnode, so the armed timer IS cancelled-and-leaked
there.)

## LATENT — the module does not build on DragonFly
`ng_car.c` was never ported: it uses FreeBSD-only `struct bintime`,
`getbinuptime()`, `bintime_sub()`, `struct mtx`, `mtx_init/mtx_destroy/
mtx_lock/mtx_unlock` — none of which exist anywhere else in the DragonFly
tree (verified: `rg` over sys/ finds `getbinuptime` only inside ng_car.c).
`sys/conf/files:1696` lists it as `optional netgraph7_car` and no kernel
config in sys/config/ enables that option.

In-guest build attempt (this pack, build.log): 295 lines, 68 compile errors
(`-Werror`), first fatal at `struct bintime lastRefill` (ng_car.c:62) /
`getbinuptime` implicit (ng_car.c:592).

## Trigger (once the module is ported / on FreeBSD-derived ng7)
root (control socket is privilege-gated, ng_socket.c:182):
```
ngctl mkpeer ether:lower car lower upper?   # any graph with a car node
ngctl msg car: setconf '{ downstream={ mode=3 cbs=8192 ebs=8192 cir=100 } ... }'  # SHAPE
# send data until queue non-empty and timer armed
ngctl rmhook <hook>                          # rmnode → shutdown → ng_uncallout → leak
# repeat; watch: vmstat -m | grep netgraph   (monotonic growth, never returned)
```
Unprivileged users cannot drive this one (unlike DF-3523, where the
unprivileged data stream armed/cancelled the timers): arm requires SHAPE
traffic, cancel only happens at node destruction — both are control-plane
(root) operations. Hence Low severity even before the build issue.

## Fix
Two layers, both required (identical to the validated DF-3523 fix):
1. kern_timeout contract fix (DF-3521/DF-3515, kern_timeout.diff in
   findings/poc/DF-3523/) so armed-cancels report rval>0;
2. ng_base.c ng_uncallout actually calls ng_free_item() — see fix.diff here
   (plus the oitem rearm free for completeness).
Plus a port of ng_car.c to DragonFly time/lock APIs (or removal of the dead
file) so the module can build at all.
