DF-0615 / fix.diff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | 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); } |