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); }