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

ip_fw3_ctl discards ip_fw3_ctl_x() error in the IP_FW_X case: nested opcode failures are reported as success to the firewall operator

Summary

In ip_fw3_ctl(), every sub-handler's return value is assigned to error except the IP_FW_X case, which calls ip_fw3_ctl_x(sopt) and discards its return. ip_fw3_ctl_x rewrites sopt_name to the payload opcode and re-enters ip_fw3_ctl; when that inner dispatch fails (e.g. an IP_FW_ADD rejected by the size check, or IP_FW_ZERO returning EINVAL), the failure propagates up to ip_fw3_ctl_x but is dropped, so setsockopt returns 0 and the ipfw3 CLI prints success for a rule that was not installed. Root-only (raw socket). A firewall administrator using IP_FW_X gets a success acknowledgement for operations that silently failed - a security policy (deny rule, state delete, table append) may be believed active while it was never installed: policy-integrity/correctness hazard. Info hardening; not verified. Fix: 'error = ip_fw3_ctl_x(sopt);' like every sibling case. [VERIFIED pass2: reproduced - nested IP_FW_X payload opcode returns 0 while kernel logs inner EINVAL; fixed module returns minus-1 EINVAL A/B]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3391 Β· 7 files
FileTypeDescriptionSize
README.md β€” 229 B ↓ raw
df3391.c β€” 1.7 KB view raw
fix.diff β€” 323 B view raw
manifest.json β€” 419 B view raw
run.log β€” 282 B view raw
run3391.sh β€” 353 B view raw
verdict.json β€” 1.9 KB view raw

DF-3391 evidence pack

What

ip_fw3_ctl discards ip_fw3_ctl_x() error in the IP_FW_X case

Verdict

status=reproduced impact=none confidence=certain

See VERDICT.md for the full narrative and fix.diff for the verified fix.

Fix verification

fixed

A/B complete with the module rebuilt from the fixed tree: nested IP_FW_X opcode now returns -1/EINVAL (stock returned 0 while the kernel logged the inner EINVAL).

↓ fix.diffper-fix-DF-3391

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md with path:line citations; run.log where a live demo exists; fix.diff

PoC changes

initial probe used a flat invalid opcode (propagates correctly through the glue); fixed to a nested IP_FW_X payload opcode which is the only path that reaches the discarding case

Verified recommended fix

error = ip_fw3_ctl_x(sopt) in the IP_FW_X case

Verdict

Reproduced on the stock INVARIANTS guest (kldload ipfw3; note loading ipfw3 enables default-deny, so the probe runs detached and unloads the module). A NESTED opcode is required: setsockopt(IP_FW_X) with payload opcode IP_FW_X(49) re-enters ip_fw3_ctl_x, whose inner failure (invalid innermost opcode 0xBEEF -> EINVAL, kernel logs ip_fw3_ctl invalid option 48879) is DROPPED at ip_fw3.c:1058-1059 and setsockopt returns 0 - the firewall operator gets success for an operation that failed. Control case: flat invalid opcode through the glue propagates EINVAL correctly. First probe (flat opcode) hit the glue path and refuted nothing - the bug needs the nested dispatch; second probe reproduced. Post-fix A/B on the F40FIX-built ipfw3.ko: nested case returns -1/EINVAL.