ipfw3 log-if subsystem is dead code wired to a signature-broken event handler: 'log' actions never log, log_if_lock is never initialized, and the ifnet_detach_event registration mis-invokes ip_fw3_log_clone_destroy with the cloner context as its ifp
Summary
ip_fw3_log_modevent() - the only attacher of the 'ipfw' cloner and registrar of the ifnet_detach_event handler - is never called by ipfw3_basic's module glue (which calls only state+table glue), and ip_fw3_log_ptr is never assigned anywhere, so: 'log N' on accept/deny rules silently does nothing (detective-control failure); log_if_lock is never lockinit'd (latent lockmgr-on-zeroed-lock hazard if ever called); and DF-0748's unchecked log_if_table[id] read is doubly unreachable. Additionally, the registration mismatches the handler type: ifnet_detach_event invokes eh_func(ctx, ifp) but ip_fw3_log_clone_destroy is int(struct ifnet*), so if ever wired it receives &ip_fw3_log_cloner as its ifp and drops the real ifnet - guest-measured: its 'if_dunit' reads low32 of the ifc_destroy field, always out of [0,10), so the intended detach cleanup can never run on any realistic build. Demonstrated empirically on the audit guest by force-wiring the subsystem with a shim KLD (pack): cloner works, event handler fires on every detach and no-ops. Direct impact: silent failure of ipfw3 logging (audit/evasion-relevant misexpectation). Fix: wire the modevent, assign the ptr, fix the registration to a correctly-signed wrapper.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3465 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| Makefile.wireshim | file | 56 B | β download | |
| REFUTATION.md | file | 3.9 KB | β raw | |
| run.sh | file | 1002 B | view raw | |
| verdict.json | file | 1.8 KB | view raw | |
| wireshim.c | file | 2.5 KB | view raw |
REFUTATION RECORD (kept for DF-0748 triage) β candidate "ifnet_detach_event
double-free in ip_fw3_log.c" β investigated as reserved DF-3465, refuted.
Candidate (pass-2 hypothesis)
ip_fw3_log_clone_destroy() (sys/net/ipfw3_basic/ip_fw3_log.c:182-206) is
registered as a handler for the GLOBAL ifnet_detach_event
(ip_fw3_log.c:222-225) with the same signature as the cloner's
ifc_destroy. Hypothesis: any interface detach/rename with if_dunit
colliding with a live ipfwN clone unit would type-confuse the victim ifnet
into bpfdetach()/if_detach()/if_free() β recursive double detach + double
free (and via SIOCSIFNAME, if.c:2206, a mid-rename full detach+free).
Why it is NOT reachable in this tree (two independent blockers)
Blocker 1 β the whole ipfw3 log-if subsystem is dead code
ip_fw3_log_modevent()(ip_fw3_log.c:213) β the only function that attaches the "ipfw" cloner and registers the event handler β has no caller anywhere in the tree.ipfw3_basic_modevent()(sys/net/ipfw3_basic/ip_fw3_basic.c:644-660) wires onlyip_fw3_state_modeventandip_fw3_table_modevent, NOT the log one.ip_fw3_log_ptr(sys/net/ipfw3/ip_fw3.c:134) is initialized to NULL and never assigned;check_accept/check_deny(ip_fw3.c:252,263) guard on it and therefore never callip_fw3_log()β so DF-0748's OOB table index is equally unreachable in-tree. Consequence:log_if_lockis neverlockinit'd andlog_if_table[]stays all-NULL. (Filed separately as an Info/Low dormancy finding.)
Blocker 2 β even when wired, the handler is a guaranteed silent no-op
ifnet_detach_event_handler_t is void (*)(void *, struct ifnet *)
(sys/net/if_var.h:724) and EVENTHANDLER_INVOKE calls
eh_func(ee_arg, args...) β ctx FIRST, payload second
(sys/sys/eventhandler.h:123). ip_fw3_log_clone_destroy is
int (*)(struct ifnet *), so when invoked as an event handler:
- its
ifpparameter actually receives the registered context&ipfw3_log_clonerβ a staticstruct if_clone, not an ifnet; - the REAL detaching ifnet arrives as an ignored second argument.
Measured on the audit guest (kernel X86_64_GENERIC #0, module base
0xffffffff8264a000, ipfw3_log_cloner @ +0xc0 from the .ko symtab):
offsetof(struct ifnet, if_dunit) = 56
sizeof(struct if_clone) = 64 (ifc_name@16, ifc_minifs@24, ifc_maxunit@28)
struct if_clone field @56 = low 4 bytes of ifc_destroy
= low32(&ip_fw3_log_clone_destroy) ~ 0x8264xxxx
So the fake if_dunit is the low half of a kernel text pointer (>=
0x80000000 on every x86_64/i386 DFly link), which is always >= LOG_IF_MAX
(10): the handler returns EINVAL on every invocation. Empirically confirmed:
with the subsystem force-wired by wireshim.ko (below), destroying/renaming
interfaces fires the handler (my tracer saw every event) and nothing
happened; ipfw0 create/destroy via the cloner kept working.
Artifacts here
wireshim.cβ KLD that performs exactly the missing one-line wiring (ip_fw3_log_modevent(MOD_LOAD)) plus a correctly-signed tracer handler. Used to empirically demonstrate blockers 1+2 on the guest.run.shβ the trigger sequence that was tried (destroy + rename paths).- Guest observations (dmesg/serial) are quoted in this file; the double-free never manifested.
Incidental guest observation (different file, NOT this candidate)
While unloading the wired stack, the guest panicked in rn_flush β this is
ip_fw3_table_fini_dispatch() (sys/net/ipfw3_basic/ip_fw3_table.c:576-587)
calling rn_flush(table_ctx->node, ...) for every table id although
table_ctx->node is only rn_inithead'd when a table is created
(ip_fw3_table.c:96-107). kldunload ipfw3_basic with zero configured
tables NULL-derefs (rn_flush(NULL) -> read of 0x28). Root-triggerable
local DoS, out of this batch's file scope β reported to the orchestrator in
the JSON notes for a dedicated audit.
Confirmed kernel references
Detail
Exploit chain
n/a (dead code; demonstration only)
Evidence (decisive lines)
['findings/poc/DF-3465/REFUTATION.md', 'findings/poc/DF-3465/wireshim.c', 'findings/poc/DF-3465/run.sh']
PoC changes
verdict.json reconstructed by the orchestrator (pack held REFUTATION.md/wireshim/run.sh but no verdict.json).
Verified recommended fix
Wire the modevent + assign the function pointer + correctly-signed wrapper for the ifnet_detach_event registration; validate log_if_table id bounds per DF-0748.
Verdict
Dead-code functional defect demonstrated empirically: force-wired the subsystem with a shim KLD (wireshim.c) on the guest - the cloner works (ipfw0 create/destroy fine), the event handler fires on every detach and no-ops (signature mismatch: receives the cloner as its ifp; 'if_dunit' reads low32 of the ifc_destroy field, always out of [0,10)). As shipped, 'log N' actions silently do nothing (ip_fw3_log_modevent never called by the module glue; ip_fw3_log_ptr never assigned). DF-0748's OOB read is doubly unreachable. No memory corruption - silent detective-control failure + latent never-initialized lock.
No comments yet.