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

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0474 Β· 10 files
FileTypeDescriptionSize
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
README.md readme human reproduce doc
↓ download 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
VERDICT.md verdict full narrative with fix validation
↓ download raw

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)

  1. sysctl net.filters_default_to_accept=1 (keep SSH alive).
  2. kldload ipfw3 + ipfw3_basic.
  3. Install a rule with cmd[0].len = 0x80 (F_NOT | F_LEN=0), cmd_len=2.
  4. kldunload ipfw3_basic β†’ ip_fw3_unregister_module enters the inner loop at :201-204, F_LEN(cmd)==0 β†’ cmdlen=0 β†’ infinite spin. The kldunload(2) syscall never returns. timeout 10 cannot 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 in ip_fw3_chk). No panic (it's a pure infinite loop). boot.log shows 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 in ip_fw3_unregister_module.
  • After (fixed ipfw3.ko): same buggy rule installed, nc -u 10.0.2.2 9 returned rc=0 (packet passed), SSH alive, date printed. The if (cmdlen == 0) break; causes ip_fw3_chk to skip the malformed rule immediately.
  • (The kldunload validation path hit an unrelated rn_flush routing-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

fixed

validated

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.