DragonFlyBSD Kernel Audit
DF-0572 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/net/ipfw3_nat/ip_fw3_nat.c b/sys/net/ipfw3_nat/ip_fw3_nat.c
--- a/sys/net/ipfw3_nat/ip_fw3_nat.c
+++ b/sys/net/ipfw3_nat/ip_fw3_nat.c
@@ -155,16 +155,18 @@
 
 	nat_ctx = ip_fw3_nat_ctx[mycpuid];
 	(*args)->rule = *f;
-	nat = ((ipfw_insn_nat *)cmd)->nat;
+	/*
+	 * DF-0572: do NOT cache the per-CPU cfg_nat pointer in the shared
+	 * rule instruction.  Each CPU must resolve its OWN cfg_nat every
+	 * time, otherwise all CPUs converge on one CPU's cfg_nat and race
+	 * unsynchronized on its RB trees (and against that CPU's cleanup).
+	 */
+	nat_id = cmd->arg1;
+	nat = nat_ctx->nats[nat_id - 1];
 	if (nat == NULL) {
-		nat_id = cmd->arg1;
-		nat = nat_ctx->nats[nat_id - 1];
-		if (nat == NULL) {
-			*cmd_val = IP_FW_DENY;
-			*cmd_ctl = IP_FW_CTL_DONE;
-			return;
-		}
-		((ipfw_insn_nat *)cmd)->nat = nat;
+		*cmd_val = IP_FW_DENY;
+		*cmd_ctl = IP_FW_CTL_DONE;
+		return;
 	}
 	*cmd_val = ip_fw3_nat(*args, nat, (*args)->m);
 	*cmd_ctl = IP_FW_CTL_NAT;