diff options
author | Pavel Emelyanov <xemul@openvz.org> | 2007-10-18 00:22:42 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2007-10-18 00:22:42 -0400 |
commit | 47e958eac280c263397582d5581e868c3227a1bd (patch) | |
tree | 67fc50a26b7861b3330eae7f1874df851c0ef740 /net | |
parent | d3904b739928edd83d117f1eb5bfa69f18d6f046 (diff) |
[NET]: Fix the race between sk_filter_(de|at)tach and sk_clone()
The proposed fix is to delay the reference counter decrement
until the quiescent state pass. This will give sk_clone() a
chance to get the reference on the cloned filter.
Regular sk_filter_uncharge can happen from the sk_free() only
and there's no need in delaying the put - the socket is dead
anyway and is to be release itself.
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/core/filter.c | 23 |
1 files changed, 21 insertions, 2 deletions
diff --git a/net/core/filter.c b/net/core/filter.c index 54dddc92452d..1f0068eae501 100644 --- a/net/core/filter.c +++ b/net/core/filter.c | |||
@@ -387,6 +387,25 @@ int sk_chk_filter(struct sock_filter *filter, int flen) | |||
387 | } | 387 | } |
388 | 388 | ||
389 | /** | 389 | /** |
390 | * sk_filter_rcu_release: Release a socket filter by rcu_head | ||
391 | * @rcu: rcu_head that contains the sk_filter to free | ||
392 | */ | ||
393 | static void sk_filter_rcu_release(struct rcu_head *rcu) | ||
394 | { | ||
395 | struct sk_filter *fp = container_of(rcu, struct sk_filter, rcu); | ||
396 | |||
397 | sk_filter_release(fp); | ||
398 | } | ||
399 | |||
400 | static void sk_filter_delayed_uncharge(struct sock *sk, struct sk_filter *fp) | ||
401 | { | ||
402 | unsigned int size = sk_filter_len(fp); | ||
403 | |||
404 | atomic_sub(size, &sk->sk_omem_alloc); | ||
405 | call_rcu_bh(&fp->rcu, sk_filter_rcu_release); | ||
406 | } | ||
407 | |||
408 | /** | ||
390 | * sk_attach_filter - attach a socket filter | 409 | * sk_attach_filter - attach a socket filter |
391 | * @fprog: the filter program | 410 | * @fprog: the filter program |
392 | * @sk: the socket to use | 411 | * @sk: the socket to use |
@@ -428,7 +447,7 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) | |||
428 | rcu_assign_pointer(sk->sk_filter, fp); | 447 | rcu_assign_pointer(sk->sk_filter, fp); |
429 | rcu_read_unlock_bh(); | 448 | rcu_read_unlock_bh(); |
430 | 449 | ||
431 | sk_filter_uncharge(sk, old_fp); | 450 | sk_filter_delayed_uncharge(sk, old_fp); |
432 | return 0; | 451 | return 0; |
433 | } | 452 | } |
434 | 453 | ||
@@ -441,7 +460,7 @@ int sk_detach_filter(struct sock *sk) | |||
441 | filter = rcu_dereference(sk->sk_filter); | 460 | filter = rcu_dereference(sk->sk_filter); |
442 | if (filter) { | 461 | if (filter) { |
443 | rcu_assign_pointer(sk->sk_filter, NULL); | 462 | rcu_assign_pointer(sk->sk_filter, NULL); |
444 | sk_filter_uncharge(sk, filter); | 463 | sk_filter_delayed_uncharge(sk, filter); |
445 | ret = 0; | 464 | ret = 0; |
446 | } | 465 | } |
447 | rcu_read_unlock_bh(); | 466 | rcu_read_unlock_bh(); |