[NET]: Fix sk->sk_filter field access
Function sk_filter() is called from tcp_v{4,6}_rcv() functions with arg needlock = 0, while socket is not locked at that moment. In order to avoid this and similar issues in the future, use rcu for sk->sk_filter field read protection. Signed-off-by: Dmitry Mishin <dim@openvz.org> Signed-off-by: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> Signed-off-by: Kirill Korotaev <dev@openvz.org>
This commit is contained in:
committed by
David S. Miller
parent
dc435e6dac
commit
fda9ef5d67
@ -422,10 +422,10 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
|
||||
if (!err) {
|
||||
struct sk_filter *old_fp;
|
||||
|
||||
spin_lock_bh(&sk->sk_lock.slock);
|
||||
old_fp = sk->sk_filter;
|
||||
sk->sk_filter = fp;
|
||||
spin_unlock_bh(&sk->sk_lock.slock);
|
||||
rcu_read_lock_bh();
|
||||
old_fp = rcu_dereference(sk->sk_filter);
|
||||
rcu_assign_pointer(sk->sk_filter, fp);
|
||||
rcu_read_unlock_bh();
|
||||
fp = old_fp;
|
||||
}
|
||||
|
||||
|
@ -247,11 +247,7 @@ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* It would be deadlock, if sock_queue_rcv_skb is used
|
||||
with socket lock! We assume that users of this
|
||||
function are lock free.
|
||||
*/
|
||||
err = sk_filter(sk, skb, 1);
|
||||
err = sk_filter(sk, skb);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
@ -278,7 +274,7 @@ int sk_receive_skb(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
int rc = NET_RX_SUCCESS;
|
||||
|
||||
if (sk_filter(sk, skb, 0))
|
||||
if (sk_filter(sk, skb))
|
||||
goto discard_and_relse;
|
||||
|
||||
skb->dev = NULL;
|
||||
@ -606,15 +602,15 @@ set_rcvbuf:
|
||||
break;
|
||||
|
||||
case SO_DETACH_FILTER:
|
||||
spin_lock_bh(&sk->sk_lock.slock);
|
||||
filter = sk->sk_filter;
|
||||
rcu_read_lock_bh();
|
||||
filter = rcu_dereference(sk->sk_filter);
|
||||
if (filter) {
|
||||
sk->sk_filter = NULL;
|
||||
spin_unlock_bh(&sk->sk_lock.slock);
|
||||
rcu_assign_pointer(sk->sk_filter, NULL);
|
||||
sk_filter_release(sk, filter);
|
||||
rcu_read_unlock_bh();
|
||||
break;
|
||||
}
|
||||
spin_unlock_bh(&sk->sk_lock.slock);
|
||||
rcu_read_unlock_bh();
|
||||
ret = -ENONET;
|
||||
break;
|
||||
|
||||
@ -884,10 +880,10 @@ void sk_free(struct sock *sk)
|
||||
if (sk->sk_destruct)
|
||||
sk->sk_destruct(sk);
|
||||
|
||||
filter = sk->sk_filter;
|
||||
filter = rcu_dereference(sk->sk_filter);
|
||||
if (filter) {
|
||||
sk_filter_release(sk, filter);
|
||||
sk->sk_filter = NULL;
|
||||
rcu_assign_pointer(sk->sk_filter, NULL);
|
||||
}
|
||||
|
||||
sock_disable_timestamp(sk);
|
||||
|
Reference in New Issue
Block a user