⬢ DragonFlyBSD Kernel Audit
DF-3265 / fix.diff
← back to finding ↓ download raw
--- a/sys/net/pf/pf.c
+++ b/sys/net/pf/pf.c
@@ -911,10 +911,12 @@ pf_state_key_attach(struct pfi_kif *kif, struct pf_state_key *sk,
 	if ((si = kmalloc(sizeof(struct pf_state_item),
 			  M_PFSTATEITEMPL, M_NOWAIT)) == NULL) {
-		pf_state_key_detach(s, idx);
+		/*
+		 * We hold pf_global_statetbl_lock exclusively for
+		 * PFSTATE_STACK_GLOBAL states; pf_state_key_detach()
+		 * would re-acquire it -> lockmgr "locking against
+		 * myself" panic.  Use the non-locking variant here.
+		 */
+		pf_state_key_detach_unlocked(s, idx);
 		error = -1;
 		goto failed;	/* collision! */
 	}
--- a/sys/net/pf/pf.c (helper)
+++ b/sys/net/pf/pf.c
@@ new helper before pf_state_key_detach
+/*
+ * Like pf_state_key_detach() but MUST be called with the appropriate
+ * statetbl lock (global or per-cpu) already held exclusively.
+ */
+static void
+pf_state_key_detach_unlocked(struct pf_state *s, int idx)
+{
+	struct pf_state_item *si;
+	int cpu;
+
+	if (s->state_flags & PFSTATE_STACK_GLOBAL) {
+		cpu = ncpus;
+	} else {
+		cpu = mycpu->gd_cpuid;
+	}
+	si = TAILQ_FIRST(&s->key[idx]->states);
+	while (si && si->s != s)
+		si = TAILQ_NEXT(si, entry);
+	if (si) {
+		TAILQ_REMOVE(&s->key[idx]->states, si, entry);
+		kfree(si, M_PFSTATEITEMPL);
+	}
+	if (TAILQ_EMPTY(&s->key[idx]->states)) {
+		RB_REMOVE(pf_state_tree, &pf_statetbl[cpu], s->key[idx]);
+		pf_state_key_detach_release(s, idx);
+	}
+}