DF-0473 / fix.diff
diff --git a/sys/net/ipfw3/ip_fw3.c b/sys/net/ipfw3/ip_fw3.c --- a/sys/net/ipfw3/ip_fw3.c +++ b/sys/net/ipfw3/ip_fw3.c @@ -503,6 +503,21 @@ } check_body: /* check the body of the rule again.*/ + /* + * DF-0473: cmd->module / cmd->opcode are attacker- + * controlled uint8_t (0..255) and were never bounds- + * checked, so a crafted rule indexed past the + * filter_funcs[MAX_MODULE=10][MAX_OPCODE_PER_MODULE=100] + * array (1000 entries) and called an arbitrary pointer. + * Reject out-of-range indices and unregistered opcodes + * (NULL slot) by skipping the instruction. + */ + if (cmd->module >= MAX_MODULE || + cmd->opcode >= MAX_OPCODE_PER_MODULE || + filter_funcs[cmd->module][cmd->opcode] == NULL) { + cmd_val = 0; + goto next_cmd; + } (filter_funcs[cmd->module][cmd->opcode]) (&cmd_ctl, &cmd_val, &args, &f, cmd, ip_len); switch(cmd_ctl) { @@ -964,6 +979,29 @@ } ioc_rule = sopt->sopt_val; + /* + * DF-0473 (defense-in-depth): reject up front any rule whose + * instruction carries an out-of-range module/opcode, which would + * otherwise index past filter_funcs[MAX_MODULE][MAX_OPCODE_PER_MODULE] + * when the firewall evaluates a packet. Walk every instruction in + * the rule using the same F_LEN() stepping as ip_fw3_chk. + */ + { + ipfw_insn *__c; + int __l, __cl; + for (__l = ioc_rule->cmd_len, __c = ioc_rule->cmd; __l > 0; + __l -= __cl, + __c = (ipfw_insn *)((uint32_t *)__c + __cl)) { + __cl = F_LEN(__c); + if (__cl == 0) + break; + if (__c->module >= MAX_MODULE || + __c->opcode >= MAX_OPCODE_PER_MODULE) { + return EINVAL; + } + } + } + ip_fw3_add_rule(ioc_rule); return 0; } |