aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorDmitry Mishin <dim@openvz.org>2006-08-31 18:28:39 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2006-09-22 18:18:47 -0400
commitfda9ef5d679b07c9d9097aaf6ef7f069d794a8f9 (patch)
tree6a265dc2038bc5568c5a499e6c8d4733650ed3f7 /net
parentdc435e6dac1439340eaeceef84022c4e4749796d (diff)
[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>
Diffstat (limited to 'net')
-rw-r--r--net/core/filter.c8
-rw-r--r--net/core/sock.c22
-rw-r--r--net/dccp/ipv6.c2
-rw-r--r--net/decnet/dn_nsp_in.c2
-rw-r--r--net/ipv4/tcp_ipv4.c2
-rw-r--r--net/ipv6/tcp_ipv6.c4
-rw-r--r--net/packet/af_packet.c43
-rw-r--r--net/sctp/input.c2
8 files changed, 37 insertions, 48 deletions
diff --git a/net/core/filter.c b/net/core/filter.c
index 5b4486a60cf6..6732782a5a40 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -422,10 +422,10 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
422 if (!err) { 422 if (!err) {
423 struct sk_filter *old_fp; 423 struct sk_filter *old_fp;
424 424
425 spin_lock_bh(&sk->sk_lock.slock); 425 rcu_read_lock_bh();
426 old_fp = sk->sk_filter; 426 old_fp = rcu_dereference(sk->sk_filter);
427 sk->sk_filter = fp; 427 rcu_assign_pointer(sk->sk_filter, fp);
428 spin_unlock_bh(&sk->sk_lock.slock); 428 rcu_read_unlock_bh();
429 fp = old_fp; 429 fp = old_fp;
430 } 430 }
431 431
diff --git a/net/core/sock.c b/net/core/sock.c
index cfaf09039b02..b77e155cbe6c 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -247,11 +247,7 @@ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
247 goto out; 247 goto out;
248 } 248 }
249 249
250 /* It would be deadlock, if sock_queue_rcv_skb is used 250 err = sk_filter(sk, skb);
251 with socket lock! We assume that users of this
252 function are lock free.
253 */
254 err = sk_filter(sk, skb, 1);
255 if (err) 251 if (err)
256 goto out; 252 goto out;
257 253
@@ -278,7 +274,7 @@ int sk_receive_skb(struct sock *sk, struct sk_buff *skb)
278{ 274{
279 int rc = NET_RX_SUCCESS; 275 int rc = NET_RX_SUCCESS;
280 276
281 if (sk_filter(sk, skb, 0)) 277 if (sk_filter(sk, skb))
282 goto discard_and_relse; 278 goto discard_and_relse;
283 279
284 skb->dev = NULL; 280 skb->dev = NULL;
@@ -606,15 +602,15 @@ set_rcvbuf:
606 break; 602 break;
607 603
608 case SO_DETACH_FILTER: 604 case SO_DETACH_FILTER:
609 spin_lock_bh(&sk->sk_lock.slock); 605 rcu_read_lock_bh();
610 filter = sk->sk_filter; 606 filter = rcu_dereference(sk->sk_filter);
611 if (filter) { 607 if (filter) {
612 sk->sk_filter = NULL; 608 rcu_assign_pointer(sk->sk_filter, NULL);
613 spin_unlock_bh(&sk->sk_lock.slock);
614 sk_filter_release(sk, filter); 609 sk_filter_release(sk, filter);
610 rcu_read_unlock_bh();
615 break; 611 break;
616 } 612 }
617 spin_unlock_bh(&sk->sk_lock.slock); 613 rcu_read_unlock_bh();
618 ret = -ENONET; 614 ret = -ENONET;
619 break; 615 break;
620 616
@@ -884,10 +880,10 @@ void sk_free(struct sock *sk)
884 if (sk->sk_destruct) 880 if (sk->sk_destruct)
885 sk->sk_destruct(sk); 881 sk->sk_destruct(sk);
886 882
887 filter = sk->sk_filter; 883 filter = rcu_dereference(sk->sk_filter);
888 if (filter) { 884 if (filter) {
889 sk_filter_release(sk, filter); 885 sk_filter_release(sk, filter);
890 sk->sk_filter = NULL; 886 rcu_assign_pointer(sk->sk_filter, NULL);
891 } 887 }
892 888
893 sock_disable_timestamp(sk); 889 sock_disable_timestamp(sk);
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index f9c5e12d7038..7a47399cf31f 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -970,7 +970,7 @@ static int dccp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
970 if (skb->protocol == htons(ETH_P_IP)) 970 if (skb->protocol == htons(ETH_P_IP))
971 return dccp_v4_do_rcv(sk, skb); 971 return dccp_v4_do_rcv(sk, skb);
972 972
973 if (sk_filter(sk, skb, 0)) 973 if (sk_filter(sk, skb))
974 goto discard; 974 goto discard;
975 975
976 /* 976 /*
diff --git a/net/decnet/dn_nsp_in.c b/net/decnet/dn_nsp_in.c
index 86f7f3b28e70..72ecc6e62ec4 100644
--- a/net/decnet/dn_nsp_in.c
+++ b/net/decnet/dn_nsp_in.c
@@ -586,7 +586,7 @@ static __inline__ int dn_queue_skb(struct sock *sk, struct sk_buff *skb, int sig
586 goto out; 586 goto out;
587 } 587 }
588 588
589 err = sk_filter(sk, skb, 0); 589 err = sk_filter(sk, skb);
590 if (err) 590 if (err)
591 goto out; 591 goto out;
592 592
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 23b46e36b147..39b179856082 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1104,7 +1104,7 @@ process:
1104 goto discard_and_relse; 1104 goto discard_and_relse;
1105 nf_reset(skb); 1105 nf_reset(skb);
1106 1106
1107 if (sk_filter(sk, skb, 0)) 1107 if (sk_filter(sk, skb))
1108 goto discard_and_relse; 1108 goto discard_and_relse;
1109 1109
1110 skb->dev = NULL; 1110 skb->dev = NULL;
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 2b18918f3011..2546fc9f0a78 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1075,7 +1075,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
1075 if (skb->protocol == htons(ETH_P_IP)) 1075 if (skb->protocol == htons(ETH_P_IP))
1076 return tcp_v4_do_rcv(sk, skb); 1076 return tcp_v4_do_rcv(sk, skb);
1077 1077
1078 if (sk_filter(sk, skb, 0)) 1078 if (sk_filter(sk, skb))
1079 goto discard; 1079 goto discard;
1080 1080
1081 /* 1081 /*
@@ -1232,7 +1232,7 @@ process:
1232 if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) 1232 if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
1233 goto discard_and_relse; 1233 goto discard_and_relse;
1234 1234
1235 if (sk_filter(sk, skb, 0)) 1235 if (sk_filter(sk, skb))
1236 goto discard_and_relse; 1236 goto discard_and_relse;
1237 1237
1238 skb->dev = NULL; 1238 skb->dev = NULL;
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 300215bdbf46..f4ccb90e6739 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -427,21 +427,24 @@ out_unlock:
427} 427}
428#endif 428#endif
429 429
430static inline unsigned run_filter(struct sk_buff *skb, struct sock *sk, unsigned res) 430static inline int run_filter(struct sk_buff *skb, struct sock *sk,
431 unsigned *snaplen)
431{ 432{
432 struct sk_filter *filter; 433 struct sk_filter *filter;
434 int err = 0;
433 435
434 bh_lock_sock(sk); 436 rcu_read_lock_bh();
435 filter = sk->sk_filter; 437 filter = rcu_dereference(sk->sk_filter);
436 /* 438 if (filter != NULL) {
437 * Our caller already checked that filter != NULL but we need to 439 err = sk_run_filter(skb, filter->insns, filter->len);
438 * verify that under bh_lock_sock() to be safe 440 if (!err)
439 */ 441 err = -EPERM;
440 if (likely(filter != NULL)) 442 else if (*snaplen > err)
441 res = sk_run_filter(skb, filter->insns, filter->len); 443 *snaplen = err;
442 bh_unlock_sock(sk); 444 }
445 rcu_read_unlock_bh();
443 446
444 return res; 447 return err;
445} 448}
446 449
447/* 450/*
@@ -491,13 +494,8 @@ static int packet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet
491 494
492 snaplen = skb->len; 495 snaplen = skb->len;
493 496
494 if (sk->sk_filter) { 497 if (run_filter(skb, sk, &snaplen) < 0)
495 unsigned res = run_filter(skb, sk, snaplen); 498 goto drop_n_restore;
496 if (res == 0)
497 goto drop_n_restore;
498 if (snaplen > res)
499 snaplen = res;
500 }
501 499
502 if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >= 500 if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >=
503 (unsigned)sk->sk_rcvbuf) 501 (unsigned)sk->sk_rcvbuf)
@@ -593,13 +591,8 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packe
593 591
594 snaplen = skb->len; 592 snaplen = skb->len;
595 593
596 if (sk->sk_filter) { 594 if (run_filter(skb, sk, &snaplen) < 0)
597 unsigned res = run_filter(skb, sk, snaplen); 595 goto drop_n_restore;
598 if (res == 0)
599 goto drop_n_restore;
600 if (snaplen > res)
601 snaplen = res;
602 }
603 596
604 if (sk->sk_type == SOCK_DGRAM) { 597 if (sk->sk_type == SOCK_DGRAM) {
605 macoff = netoff = TPACKET_ALIGN(TPACKET_HDRLEN) + 16; 598 macoff = netoff = TPACKET_ALIGN(TPACKET_HDRLEN) + 16;
diff --git a/net/sctp/input.c b/net/sctp/input.c
index 8a34d95602ce..03f65de75d88 100644
--- a/net/sctp/input.c
+++ b/net/sctp/input.c
@@ -228,7 +228,7 @@ int sctp_rcv(struct sk_buff *skb)
228 goto discard_release; 228 goto discard_release;
229 nf_reset(skb); 229 nf_reset(skb);
230 230
231 if (sk_filter(sk, skb, 1)) 231 if (sk_filter(sk, skb))
232 goto discard_release; 232 goto discard_release;
233 233
234 /* Create an SCTP packet structure. */ 234 /* Create an SCTP packet structure. */