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)
PoC verification
Evidence pack
findings/poc/DF-3391 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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
fixedA/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).
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.
No comments yet.