DragonFlyBSD Kernel Audit
DF-0615 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/netinet6/in6_src.c b/sys/netinet6/in6_src.c
--- a/sys/netinet6/in6_src.c
+++ b/sys/netinet6/in6_src.c
@@ -102,6 +102,12 @@
 #define ADDR_LABEL_NOTAPP (-1)
 struct in6_addrpolicy defaultaddrpolicy;
 
+/* Serializes addrsel_policytab between sysctl readers (user thread) and
+ * ioctl mutators (netisr0). Without this, a reader's TAILQ walk can
+ * dereference an entry freed by a concurrent deleter (use-after-free). */
+static struct lwkt_token addrsel_policy_token =
+	LWKT_TOKEN_INITIALIZER(addrsel_policy_token);
+
 static void	init_policy_queue(void);
 static int	add_addrsel_policyent(struct in6_addrpolicy *);
 static int	delete_addrsel_policyent(struct in6_addrpolicy *);
@@ -737,7 +743,13 @@
 add_addrsel_policyent(struct in6_addrpolicy *newpolicy)
 {
 	struct addrsel_policyent *new, *pol;
+	int error = 0;
+
+	/* Allocate before taking the token; M_WAITOK may sleep. */
+	new = kmalloc(sizeof(*new), M_IFADDR, M_WAITOK | M_ZERO);
+	new->ape_policy = *newpolicy;
 
+	lwkt_gettoken(&addrsel_policy_token);
 	/* duplication check */
 	for (pol = TAILQ_FIRST(&addrsel_policytab); pol;
 	     pol = TAILQ_NEXT(pol, ape_entry)) {
@@ -745,25 +757,26 @@
 				       &pol->ape_policy.addr) &&
 		    SA6_ARE_ADDR_EQUAL(&newpolicy->addrmask,
 				       &pol->ape_policy.addrmask)) {
-			return (EEXIST);	/* or override it? */
+			error = EEXIST;	/* or override it? */
+			goto out;
 		}
 	}
 
-	new = kmalloc(sizeof(*new), M_IFADDR, M_WAITOK | M_ZERO);
-
-	/* XXX: should validate entry */
-	new->ape_policy = *newpolicy;
-
 	TAILQ_INSERT_TAIL(&addrsel_policytab, new, ape_entry);
-
-	return (0);
+out:
+	if (error)
+		kfree(new, M_IFADDR);
+	lwkt_reltoken(&addrsel_policy_token);
+	return (error);
 }
 
 static int
 delete_addrsel_policyent(struct in6_addrpolicy *key)
 {
 	struct addrsel_policyent *pol;
+	int error = 0;
 
+	lwkt_gettoken(&addrsel_policy_token);
 	/* search for the entry in the table */
 	for (pol = TAILQ_FIRST(&addrsel_policytab); pol;
 	     pol = TAILQ_NEXT(pol, ape_entry)) {
@@ -773,13 +786,16 @@
 			break;
 		}
 	}
-	if (pol == NULL)
-		return (ESRCH);
+	if (pol == NULL) {
+		error = ESRCH;
+		goto out;
+	}
 
 	TAILQ_REMOVE(&addrsel_policytab, pol, ape_entry);
 	kfree(pol, M_IFADDR);
-
-	return (0);
+out:
+	lwkt_reltoken(&addrsel_policy_token);
+	return (error);
 }
 
 static int
@@ -788,12 +804,14 @@
 	struct addrsel_policyent *pol;
 	int error = 0;
 
+	lwkt_gettoken(&addrsel_policy_token);
 	for (pol = TAILQ_FIRST(&addrsel_policytab); pol;
 	     pol = TAILQ_NEXT(pol, ape_entry)) {
 		if ((error = (*callback)(&pol->ape_policy, w)) != 0)
-			return (error);
+			goto out;
 	}
-
+out:
+	lwkt_reltoken(&addrsel_policy_token);
 	return (error);
 }