Opcode iteration loop: F_LEN(cmd)==0 causes infinite loop hanging netisr thread
Summary
Opcode loop advances l-=cmdlen; cmd=(uint32_t*)cmd+cmdlen where cmdlen=F_LEN(cmd)=cmd->len&0x3f(ip_fw3.h:122). If opcode len low 6 bits=0 (e.g. len=0x80 F_NOT or 0x40 F_OR with zero length), cmdlen=0 -> neither l nor cmd advances -> l>0 forever. Same in ip_fw3_unregister_module(:201-204). Crafted rule with zero-length opcode permanently hangs netisr thread. Root installs rule, any matching remote traffic triggers hang. Fix: if(cmdlen==0) goto next_rule.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0474 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| df0474_install.c | trigger-source | installs the buggy rule with cmd[0].len=0x80 (F_LEN=0) | 2.9 KB | view raw |
| df0474_loop.c | trigger-source | alt PoC with UDP-only rule + packet trigger | 5.2 KB | view raw |
| fix.diff | suggested-fix | if(cmdlen==0) break; in both loops | 706 B | view raw |
| build.sh | build-log | build script | 142 B | view raw |
| run.sh | run-log | run script | 365 B | view raw |
| env.txt | environment | guest environment | 429 B | view raw |
| VERDICT.md | verdict | full narrative with fix validation | 3.5 KB | β raw |
| README.md | readme | human reproduce doc | 467 B | β raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-0474 β PoC evidence pack
See VERDICT.md for the full analysis.
Files
- trigger source β the PoC program(s)
- build.sh β exact build command
- run.sh β exact run command
- fix.diff β git-apply-able fix for the verified bug
- VERDICT.md β full narrative: mechanism, evidence, fix validation
- manifest.json β machine-readable catalog
- env.txt β guest environment
Quick reproduce
./build.sh && ./run.sh
DF-0474 β ipfw3 zero-length opcode infinite loop (DoS)
Verdict
REPRODUCED β confirmed live via the ip_fw3_unregister_module path (kldunload hang) and the ip_fw3_chk path (outgoing packet hang). Fix validated.
Mechanism
The inner opcode-iteration loop in both ip_fw3_chk (sys/net/ipfw3/ip_fw3.c:493-495) and ip_fw3_unregister_module (sys/net/ipfw3/ip_fw3.c:201-204) is:
for (l = f->cmd_len, cmd = f->cmd; l > 0;
l -= cmdlen,
cmd = (ipfw_insn *)((uint32_t *)cmd + cmdlen)) {
cmdlen = F_LEN(cmd); /* = cmd->len & 0x3f */
...
}
F_LEN(cmd) = cmd->len & 0x3f (ip_fw3.h:122). If the low 6 bits of cmd->len are 0 (e.g. len = 0x80 = F_NOT|0, or 0x40 = F_OR|0), cmdlen == 0 and neither l nor cmd advances, so l > 0 holds forever. The loop spins on the same cmd, wedging the calling thread.
Live reproduction (unfixed #0 kernel)
Path A β unregister hang (cleanest)
sysctl net.filters_default_to_accept=1(keep SSH alive).kldload ipfw3 + ipfw3_basic.- Install a rule with
cmd[0].len = 0x80(F_NOT | F_LEN=0),cmd_len=2. kldunload ipfw3_basicβip_fw3_unregister_moduleenters the inner loop at :201-204,F_LEN(cmd)==0βcmdlen=0β infinite spin. The kldunload(2) syscall never returns.timeout 10cannot kill it (the thread is stuck in kernel mode). The guest becomes unresponsive.
Path B β packet-path hang
The same rule, when reached during packet processing by ip_fw3_chk, spins the inner loop at :493-495 on the calling netisr thread. Outgoing packets through ip_output β PFIL_OUT β ip_fw3_check_out β ip_fw3_chk hang. (Loopback 127.x bypasses PFIL_OUT on DragonFly, so the trigger packet must use a non-loopback destination.)
Evidence
run.logβ the decisive run: buggy rule installed, then the SSH session became unresponsive (outgoing packets hung inip_fw3_chk). No panic (it's a pure infinite loop).boot.logshows no crash.
Threat model
Root (or any process with raw-socket + IP_FW_X setsockopt privilege) installs a malformed rule. Any matching packet then wedges the netisr / calling thread on the affected CPU. On a single-CPU deployment this is a complete IP stack DoS; on multi-CPU it degrades one CPU at a time. This is a DoS, not a privilege escalation β the primitive is an infinite loop, not memory corruption.
Fix
fix.diff β add if (cmdlen == 0) break; after cmdlen = F_LEN(cmd); in BOTH loops (chk at :495 and unregister at :204). Validated: built the fixed ipfw3.ko, loaded it, installed the buggy rule, sent a UDP packet β the packet went through cleanly (no hang), SSH stayed alive.
Fix validation
- Before (unfixed): outgoing packets after buggy-rule install hang in
ip_fw3_chk; kldunload hangs inip_fw3_unregister_module. - After (fixed ipfw3.ko): same buggy rule installed,
nc -u 10.0.2.2 9returnedrc=0(packet passed), SSH alive,dateprinted. Theif (cmdlen == 0) break;causesip_fw3_chkto skip the malformed rule immediately. - (The
kldunloadvalidation path hit an unrelatedrn_flushrouting-table panic during module unload β not caused by this fix; the chk-path validation above is the clean before/after comparison.)
Build / Run
cc -o df0474_install df0474_install.c # installs the buggy rule # then: sysctl net.filters_default_to_accept=1; kldload ipfw3; kldload ipfw3_basic # ./df0474_install # kldunload ipfw3_basic # HANGS on unfixed kernel
Fix verification
fixedvalidated
see evidence pack
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
REPRODUCED (live). ip_fw3 F_LEN(cmd)==0 infinite loop wedges netisr. Root rule install + packet. Module fix validated.
No comments yet.