aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorPavel Emelyanov <xemul@openvz.org>2007-10-18 08:18:56 -0400
committerDavid S. Miller <davem@davemloft.net>2007-10-18 08:18:56 -0400
commit78c2e50253569e62caa4a61fc1cc5a0158edec43 (patch)
tree07567a10517b1cf1ce297c1ecf4ce7c617856331 /net
parentbd0bf57700cb0eaa92f3d2ee040a69743cdd99d0 (diff)
[IPV6]: Fix race in ipv6_flowlabel_opt() when inserting two labels
In the IPV6_FL_A_GET case the hash is checked for flowlabels with the given label. If it is not found, the lock, protecting the hash, is dropped to be re-get for writing. After this a newly allocated entry is inserted, but no checks are performed to catch a classical SMP race, when the conflicting label may be inserted on another cpu. Use the (currently unused) return value from fl_intern() to return the conflicting entry (if found) and re-check, whether we can reuse it (IPV6_FL_F_EXCL) or return -EEXISTS. Also add the comment, about why not re-lookup the current sock for conflicting flowlabel entry. Signed-off-by: Pavel Emelyanov <xemul@openvz.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/ipv6/ip6_flowlabel.c34
1 files changed, 25 insertions, 9 deletions
diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c
index f40a08669db0..e55ae1a1f560 100644
--- a/net/ipv6/ip6_flowlabel.c
+++ b/net/ipv6/ip6_flowlabel.c
@@ -154,8 +154,10 @@ static void ip6_fl_gc(unsigned long dummy)
154 write_unlock(&ip6_fl_lock); 154 write_unlock(&ip6_fl_lock);
155} 155}
156 156
157static int fl_intern(struct ip6_flowlabel *fl, __be32 label) 157static struct ip6_flowlabel *fl_intern(struct ip6_flowlabel *fl, __be32 label)
158{ 158{
159 struct ip6_flowlabel *lfl;
160
159 fl->label = label & IPV6_FLOWLABEL_MASK; 161 fl->label = label & IPV6_FLOWLABEL_MASK;
160 162
161 write_lock_bh(&ip6_fl_lock); 163 write_lock_bh(&ip6_fl_lock);
@@ -163,12 +165,26 @@ static int fl_intern(struct ip6_flowlabel *fl, __be32 label)
163 for (;;) { 165 for (;;) {
164 fl->label = htonl(net_random())&IPV6_FLOWLABEL_MASK; 166 fl->label = htonl(net_random())&IPV6_FLOWLABEL_MASK;
165 if (fl->label) { 167 if (fl->label) {
166 struct ip6_flowlabel *lfl;
167 lfl = __fl_lookup(fl->label); 168 lfl = __fl_lookup(fl->label);
168 if (lfl == NULL) 169 if (lfl == NULL)
169 break; 170 break;
170 } 171 }
171 } 172 }
173 } else {
174 /*
175 * we dropper the ip6_fl_lock, so this entry could reappear
176 * and we need to recheck with it.
177 *
178 * OTOH no need to search the active socket first, like it is
179 * done in ipv6_flowlabel_opt - sock is locked, so new entry
180 * with the same label can only appear on another sock
181 */
182 lfl = __fl_lookup(fl->label);
183 if (lfl != NULL) {
184 atomic_inc(&lfl->users);
185 write_unlock_bh(&ip6_fl_lock);
186 return lfl;
187 }
172 } 188 }
173 189
174 fl->lastuse = jiffies; 190 fl->lastuse = jiffies;
@@ -176,7 +192,7 @@ static int fl_intern(struct ip6_flowlabel *fl, __be32 label)
176 fl_ht[FL_HASH(fl->label)] = fl; 192 fl_ht[FL_HASH(fl->label)] = fl;
177 atomic_inc(&fl_size); 193 atomic_inc(&fl_size);
178 write_unlock_bh(&ip6_fl_lock); 194 write_unlock_bh(&ip6_fl_lock);
179 return 0; 195 return NULL;
180} 196}
181 197
182 198
@@ -429,7 +445,8 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen)
429 struct in6_flowlabel_req freq; 445 struct in6_flowlabel_req freq;
430 struct ipv6_fl_socklist *sfl1=NULL; 446 struct ipv6_fl_socklist *sfl1=NULL;
431 struct ipv6_fl_socklist *sfl, **sflp; 447 struct ipv6_fl_socklist *sfl, **sflp;
432 struct ip6_flowlabel *fl; 448 struct ip6_flowlabel *fl, *fl1 = NULL;
449
433 450
434 if (optlen < sizeof(freq)) 451 if (optlen < sizeof(freq))
435 return -EINVAL; 452 return -EINVAL;
@@ -485,8 +502,6 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen)
485 sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL); 502 sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL);
486 503
487 if (freq.flr_label) { 504 if (freq.flr_label) {
488 struct ip6_flowlabel *fl1 = NULL;
489
490 err = -EEXIST; 505 err = -EEXIST;
491 read_lock_bh(&ip6_sk_fl_lock); 506 read_lock_bh(&ip6_sk_fl_lock);
492 for (sfl = np->ipv6_fl_list; sfl; sfl = sfl->next) { 507 for (sfl = np->ipv6_fl_list; sfl; sfl = sfl->next) {
@@ -505,6 +520,7 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen)
505 if (fl1 == NULL) 520 if (fl1 == NULL)
506 fl1 = fl_lookup(freq.flr_label); 521 fl1 = fl_lookup(freq.flr_label);
507 if (fl1) { 522 if (fl1) {
523recheck:
508 err = -EEXIST; 524 err = -EEXIST;
509 if (freq.flr_flags&IPV6_FL_F_EXCL) 525 if (freq.flr_flags&IPV6_FL_F_EXCL)
510 goto release; 526 goto release;
@@ -543,9 +559,9 @@ release:
543 if (sfl1 == NULL || (err = mem_check(sk)) != 0) 559 if (sfl1 == NULL || (err = mem_check(sk)) != 0)
544 goto done; 560 goto done;
545 561
546 err = fl_intern(fl, freq.flr_label); 562 fl1 = fl_intern(fl, freq.flr_label);
547 if (err) 563 if (fl1 != NULL)
548 goto done; 564 goto recheck;
549 565
550 if (!freq.flr_label) { 566 if (!freq.flr_label) {
551 if (copy_to_user(&((struct in6_flowlabel_req __user *) optval)->flr_label, 567 if (copy_to_user(&((struct in6_flowlabel_req __user *) optval)->flr_label,