DragonFlyBSD Kernel Audit
DF-0473 / fix.diff
← back to finding ↓ download raw
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;
 }