diff options
| author | Zach Brown <zach.brown@oracle.com> | 2006-04-10 19:05:34 -0400 |
|---|---|---|
| committer | David S. Miller <davem@sunset.davemloft.net> | 2006-04-11 20:21:05 -0400 |
| commit | f6596f9d2b4f0255f6cd68c76b85fe4cec6352af (patch) | |
| tree | 6e95cca360198b2e546904d17760ba71d1dc5ee8 | |
| parent | a145410dccdb44f81d3b56763ef9b6f721f4e47c (diff) | |
[IPv6] reassembly: Always compute hash under the fragment lock.
This closes a race where an ipq6hashfn() caller could get a hash value
and race with the cycling of the random seed. By the time they got to
the read_lock they'd have a stale hash value and might not find
previous fragments of their datagram.
This matches the previous patch to IPv4.
Signed-off-by: Zach Brown <zach.brown@oracle.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | net/ipv6/reassembly.c | 18 |
1 files changed, 12 insertions, 6 deletions
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index b67a45fb93e9..eef985e010ea 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c | |||
| @@ -121,6 +121,10 @@ static __inline__ void fq_unlink(struct frag_queue *fq) | |||
| 121 | write_unlock(&ip6_frag_lock); | 121 | write_unlock(&ip6_frag_lock); |
| 122 | } | 122 | } |
| 123 | 123 | ||
| 124 | /* | ||
| 125 | * callers should be careful not to use the hash value outside the ipfrag_lock | ||
| 126 | * as doing so could race with ipfrag_hash_rnd being recalculated. | ||
| 127 | */ | ||
| 124 | static unsigned int ip6qhashfn(u32 id, struct in6_addr *saddr, | 128 | static unsigned int ip6qhashfn(u32 id, struct in6_addr *saddr, |
| 125 | struct in6_addr *daddr) | 129 | struct in6_addr *daddr) |
| 126 | { | 130 | { |
| @@ -324,15 +328,16 @@ out: | |||
| 324 | /* Creation primitives. */ | 328 | /* Creation primitives. */ |
| 325 | 329 | ||
| 326 | 330 | ||
| 327 | static struct frag_queue *ip6_frag_intern(unsigned int hash, | 331 | static struct frag_queue *ip6_frag_intern(struct frag_queue *fq_in) |
| 328 | struct frag_queue *fq_in) | ||
| 329 | { | 332 | { |
| 330 | struct frag_queue *fq; | 333 | struct frag_queue *fq; |
| 334 | unsigned int hash; | ||
| 331 | #ifdef CONFIG_SMP | 335 | #ifdef CONFIG_SMP |
| 332 | struct hlist_node *n; | 336 | struct hlist_node *n; |
| 333 | #endif | 337 | #endif |
| 334 | 338 | ||
| 335 | write_lock(&ip6_frag_lock); | 339 | write_lock(&ip6_frag_lock); |
| 340 | hash = ip6qhashfn(fq_in->id, &fq_in->saddr, &fq_in->daddr); | ||
| 336 | #ifdef CONFIG_SMP | 341 | #ifdef CONFIG_SMP |
| 337 | hlist_for_each_entry(fq, n, &ip6_frag_hash[hash], list) { | 342 | hlist_for_each_entry(fq, n, &ip6_frag_hash[hash], list) { |
| 338 | if (fq->id == fq_in->id && | 343 | if (fq->id == fq_in->id && |
| @@ -362,7 +367,7 @@ static struct frag_queue *ip6_frag_intern(unsigned int hash, | |||
| 362 | 367 | ||
| 363 | 368 | ||
| 364 | static struct frag_queue * | 369 | static struct frag_queue * |
| 365 | ip6_frag_create(unsigned int hash, u32 id, struct in6_addr *src, struct in6_addr *dst) | 370 | ip6_frag_create(u32 id, struct in6_addr *src, struct in6_addr *dst) |
| 366 | { | 371 | { |
| 367 | struct frag_queue *fq; | 372 | struct frag_queue *fq; |
| 368 | 373 | ||
| @@ -379,7 +384,7 @@ ip6_frag_create(unsigned int hash, u32 id, struct in6_addr *src, struct in6_addr | |||
| 379 | spin_lock_init(&fq->lock); | 384 | spin_lock_init(&fq->lock); |
| 380 | atomic_set(&fq->refcnt, 1); | 385 | atomic_set(&fq->refcnt, 1); |
| 381 | 386 | ||
| 382 | return ip6_frag_intern(hash, fq); | 387 | return ip6_frag_intern(fq); |
| 383 | 388 | ||
| 384 | oom: | 389 | oom: |
| 385 | IP6_INC_STATS_BH(IPSTATS_MIB_REASMFAILS); | 390 | IP6_INC_STATS_BH(IPSTATS_MIB_REASMFAILS); |
| @@ -391,9 +396,10 @@ fq_find(u32 id, struct in6_addr *src, struct in6_addr *dst) | |||
| 391 | { | 396 | { |
| 392 | struct frag_queue *fq; | 397 | struct frag_queue *fq; |
| 393 | struct hlist_node *n; | 398 | struct hlist_node *n; |
| 394 | unsigned int hash = ip6qhashfn(id, src, dst); | 399 | unsigned int hash; |
| 395 | 400 | ||
| 396 | read_lock(&ip6_frag_lock); | 401 | read_lock(&ip6_frag_lock); |
| 402 | hash = ip6qhashfn(id, src, dst); | ||
| 397 | hlist_for_each_entry(fq, n, &ip6_frag_hash[hash], list) { | 403 | hlist_for_each_entry(fq, n, &ip6_frag_hash[hash], list) { |
| 398 | if (fq->id == id && | 404 | if (fq->id == id && |
| 399 | ipv6_addr_equal(src, &fq->saddr) && | 405 | ipv6_addr_equal(src, &fq->saddr) && |
| @@ -405,7 +411,7 @@ fq_find(u32 id, struct in6_addr *src, struct in6_addr *dst) | |||
| 405 | } | 411 | } |
| 406 | read_unlock(&ip6_frag_lock); | 412 | read_unlock(&ip6_frag_lock); |
| 407 | 413 | ||
| 408 | return ip6_frag_create(hash, id, src, dst); | 414 | return ip6_frag_create(id, src, dst); |
| 409 | } | 415 | } |
| 410 | 416 | ||
| 411 | 417 | ||
