aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorPavel Emelyanov <xemul@openvz.org>2007-10-18 00:22:42 -0400
committerDavid S. Miller <davem@davemloft.net>2007-10-18 00:22:42 -0400
commit47e958eac280c263397582d5581e868c3227a1bd (patch)
tree67fc50a26b7861b3330eae7f1874df851c0ef740 /net
parentd3904b739928edd83d117f1eb5bfa69f18d6f046 (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.c23
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 */
393static 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
400static 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();