DragonFlyBSD Kernel Audit
DF-2575 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/net/ipfw3/ip_fw3.h b/sys/net/ipfw3/ip_fw3.h
--- a/sys/net/ipfw3/ip_fw3.h
+++ b/sys/net/ipfw3/ip_fw3.h
@@ -270,6 +270,7 @@
 	uint64_t	pcnt;		/* Packet counter		*/
 	uint64_t	bcnt;		/* Byte counter			*/
 	uint32_t	timestamp;	/* tv_sec of last match		*/
+	uint32_t	refcnt;		/* reference count (dummynet)	*/
 
 	struct ip_fw 	*sibling;	/* pointer to the rule in next CPU */
 

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
@@ -583,6 +583,21 @@
 	return IP_FW_DENY;
 }
 
+/*
+ * Release a dummynet reference on a rule.  Called by dummynet (via
+ * dn_unref_priv) when a tagged packet is freed or re-dispatched.
+ * Uses atomic refcount so the unref is safe on any CPU.
+ */
+static void
+ip_fw3_unref_dn_priv(void *priv)
+{
+	struct ip_fw *rule = priv;
+
+	KKASSERT(rule->refcnt > 0);
+	if (atomic_fetchadd_int(&rule->refcnt, -1) == 1)
+		kfree(rule, M_IPFW3);
+}
+
 struct mbuf *
 ip_fw3_dummynet_io(struct mbuf *m, int pipe_nr, int dir, struct ip_fw_args *fwa)
 {
@@ -627,7 +642,14 @@
 	fid->fid_proto = id->proto;
 	fid->fid_flags = id->flags;
 
+	/*
+	 * Take a reference on the rule so it survives until dummynet
+	 * frees/re-dispatches the packet.  ip_fw3_delete_rule() will
+	 * defer the kfree until this reference is released.
+	 */
+	atomic_add_int(&fwa->rule->refcnt, 1);
 	pkt->dn_priv = fwa->rule;
+	pkt->dn_unref_priv = ip_fw3_unref_dn_priv;
 
 	if ((int)cmd->opcode == O_DUMMYNET_PIPE)
 		pkt->dn_flags |= DN_FLAGS_IS_PIPE;
@@ -651,6 +673,7 @@
 	rule->act_ofs = ioc_rule->act_ofs;
 	rule->cmd_len = ioc_rule->cmd_len;
 	rule->rulenum = ioc_rule->rulenum;
+	rule->refcnt = 1;			/* chain holds one reference	*/
 	rule->set = ioc_rule->set;
 	bcopy(ioc_rule->cmd, rule->cmd, rule->cmd_len * 4);
 
@@ -762,7 +785,11 @@
 	else
 		prev->next = rule->next;
 
-	kfree(rule, M_IPFW3);
+	/* Decrement the chain reference; defer kfree if dummynet still
+	 * holds references (in-flight tagged packets). */
+	KKASSERT(rule->refcnt > 0);
+	if (atomic_fetchadd_int(&rule->refcnt, -1) == 1)
+		kfree(rule, M_IPFW3);
 	rule = NULL;
 	return NULL;
 }
@@ -784,7 +811,9 @@
 		the_rule = rule;
 		rule = rule->next;
 
-		kfree(the_rule, M_IPFW3);
+		KKASSERT(the_rule->refcnt > 0);
+		if (atomic_fetchadd_int(&the_rule->refcnt, -1) == 1)
+			kfree(the_rule, M_IPFW3);
 	}
 
 	netisr_forwardmsg_all(&nmsg->base, mycpuid + 1);