DragonFlyBSD Kernel Audit
DF-0631 / 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
@@ -469,6 +469,9 @@
 extern ip_fw_ctl_t	*ip_fw_ctl_x_ptr;
 extern ip_fw_dn_io_t	*ip_fw_dn_io_ptr;
 
+/* DF-0631: invoked on rule deletion to drop states caching the rule ptr */
+extern void		(*ip_fw3_state_remove_rule_ptr)(struct ip_fw *);
+
 
 #define	IPFW_TABLES_MAX		32
 #define	IPFW_USR_F_NORULE	0x01
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
@@ -91,6 +91,11 @@
 
 MALLOC_DEFINE(M_IPFW3, "IPFW3", "ipfw3 module");
 
+/* DF-0631: set by ipfw3_basic; called from rule-deletion dispatches to drop
+ * states caching a soon-to-be-freed rule pointer. Defined here (module) so
+ * ipfw3_basic can resolve it via its existing MODULE_DEPEND on ipfw3. */
+void (*ip_fw3_state_remove_rule_ptr)(struct ip_fw *);
+
 #define MAX_MODULE		10
 #define MAX_OPCODE_PER_MODULE	100
 
@@ -784,6 +789,8 @@
 		the_rule = rule;
 		rule = rule->next;
 
+		if (ip_fw3_state_remove_rule_ptr)
+			ip_fw3_state_remove_rule_ptr(the_rule);
 		kfree(the_rule, M_IPFW3);
 	}
 
@@ -830,6 +837,8 @@
 	rule = ctx->rules;
 	while (rule!=NULL) {
 		if (rule->rulenum == dmsg->rulenum) {
+			if (ip_fw3_state_remove_rule_ptr)
+				ip_fw3_state_remove_rule_ptr(rule);
 			ip_fw3_delete_rule(ctx, prev, rule);
 			break;
 		}
diff --git a/sys/net/ipfw3_basic/ip_fw3_state.c b/sys/net/ipfw3_basic/ip_fw3_state.c
--- a/sys/net/ipfw3_basic/ip_fw3_state.c
+++ b/sys/net/ipfw3_basic/ip_fw3_state.c
@@ -357,6 +357,39 @@
 	return 0;
 }
 
+/* DF-0631: sweep states caching a rule ptr about to be freed. */
+void	ip_fw3_state_remove_rule(struct ip_fw *rule);
+
+void
+ip_fw3_state_remove_rule(struct ip_fw *rule)
+{
+	struct ipfw3_state_context *state_ctx = fw3_state_ctx[mycpuid];
+	struct ipfw3_state *s, *tmp;
+
+	/*
+	 * DF-0631: every dynamic state caches a raw, unrefcounted pointer to
+	 * its creating rule (s->stub).  Rule deletion must drop those states
+	 * on the owning CPU before the rule slab is freed, otherwise a later
+	 * state match restores the dangling pointer and ip_fw3 dereferences
+	 * and writes freed kernel memory.  This runs from the per-CPU rule
+	 * deletion dispatches, so it sweeps only the current CPU's trees.
+	 */
+#define	SWEEP_TREE(tree)							\
+	RB_FOREACH_SAFE(s, fw3_state_tree, &state_ctx->tree, tmp) {		\
+		if (s->stub == rule) {					\
+			RB_REMOVE(fw3_state_tree, &state_ctx->tree, s);	\
+			kfree(s, M_IPFW3_STATE);				\
+		}								\
+	}
+	SWEEP_TREE(rb_tcp_in);
+	SWEEP_TREE(rb_tcp_out);
+	SWEEP_TREE(rb_udp_in);
+	SWEEP_TREE(rb_udp_out);
+	SWEEP_TREE(rb_icmp_in);
+	SWEEP_TREE(rb_icmp_out);
+#undef SWEEP_TREE
+}
+
 void
 ip_fw3_state_flush_dispatch(netmsg_t nmsg)
 {
@@ -697,6 +730,7 @@
 	struct netmsg_base msg;
 
 	ip_fw3_ctl_state_ptr = ip_fw3_ctl_state_sockopt;
+	ip_fw3_state_remove_rule_ptr = ip_fw3_state_remove_rule;
 	callout_init_mp(&ip_fw3_state_cleanup_callout);
 	callout_reset(&ip_fw3_state_cleanup_callout,
 			sysctl_var_cleanup_interval * hz,