aboutsummaryrefslogtreecommitdiffstats
path: root/include
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 /include
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 'include')
-rw-r--r--include/linux/filter.h13
-rw-r--r--include/net/sock.h34
2 files changed, 24 insertions, 23 deletions
diff --git a/include/linux/filter.h b/include/linux/filter.h
index c6cb8f095088..91b2e3b9251e 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -25,10 +25,10 @@
25 25
26struct sock_filter /* Filter block */ 26struct sock_filter /* Filter block */
27{ 27{
28 __u16 code; /* Actual filter code */ 28 __u16 code; /* Actual filter code */
29 __u8 jt; /* Jump true */ 29 __u8 jt; /* Jump true */
30 __u8 jf; /* Jump false */ 30 __u8 jf; /* Jump false */
31 __u32 k; /* Generic multiuse field */ 31 __u32 k; /* Generic multiuse field */
32}; 32};
33 33
34struct sock_fprog /* Required for SO_ATTACH_FILTER. */ 34struct sock_fprog /* Required for SO_ATTACH_FILTER. */
@@ -41,8 +41,9 @@ struct sock_fprog /* Required for SO_ATTACH_FILTER. */
41struct sk_filter 41struct sk_filter
42{ 42{
43 atomic_t refcnt; 43 atomic_t refcnt;
44 unsigned int len; /* Number of filter blocks */ 44 unsigned int len; /* Number of filter blocks */
45 struct sock_filter insns[0]; 45 struct rcu_head rcu;
46 struct sock_filter insns[0];
46}; 47};
47 48
48static inline unsigned int sk_filter_len(struct sk_filter *fp) 49static inline unsigned int sk_filter_len(struct sk_filter *fp)
diff --git a/include/net/sock.h b/include/net/sock.h
index 337ebec84c70..edd4d73ce7f5 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -862,30 +862,24 @@ extern void sock_init_data(struct socket *sock, struct sock *sk);
862 * 862 *
863 */ 863 */
864 864
865static inline int sk_filter(struct sock *sk, struct sk_buff *skb, int needlock) 865static inline int sk_filter(struct sock *sk, struct sk_buff *skb)
866{ 866{
867 int err; 867 int err;
868 struct sk_filter *filter;
868 869
869 err = security_sock_rcv_skb(sk, skb); 870 err = security_sock_rcv_skb(sk, skb);
870 if (err) 871 if (err)
871 return err; 872 return err;
872 873
873 if (sk->sk_filter) { 874 rcu_read_lock_bh();
874 struct sk_filter *filter; 875 filter = sk->sk_filter;
875 876 if (filter) {
876 if (needlock) 877 unsigned int pkt_len = sk_run_filter(skb, filter->insns,
877 bh_lock_sock(sk); 878 filter->len);
878 879 err = pkt_len ? pskb_trim(skb, pkt_len) : -EPERM;
879 filter = sk->sk_filter;
880 if (filter) {
881 unsigned int pkt_len = sk_run_filter(skb, filter->insns,
882 filter->len);
883 err = pkt_len ? pskb_trim(skb, pkt_len) : -EPERM;
884 }
885
886 if (needlock)
887 bh_unlock_sock(sk);
888 } 880 }
881 rcu_read_unlock_bh();
882
889 return err; 883 return err;
890} 884}
891 885
@@ -897,6 +891,12 @@ static inline int sk_filter(struct sock *sk, struct sk_buff *skb, int needlock)
897 * Remove a filter from a socket and release its resources. 891 * Remove a filter from a socket and release its resources.
898 */ 892 */
899 893
894static inline void sk_filter_rcu_free(struct rcu_head *rcu)
895{
896 struct sk_filter *fp = container_of(rcu, struct sk_filter, rcu);
897 kfree(fp);
898}
899
900static inline void sk_filter_release(struct sock *sk, struct sk_filter *fp) 900static inline void sk_filter_release(struct sock *sk, struct sk_filter *fp)
901{ 901{
902 unsigned int size = sk_filter_len(fp); 902 unsigned int size = sk_filter_len(fp);
@@ -904,7 +904,7 @@ static inline void sk_filter_release(struct sock *sk, struct sk_filter *fp)
904 atomic_sub(size, &sk->sk_omem_alloc); 904 atomic_sub(size, &sk->sk_omem_alloc);
905 905
906 if (atomic_dec_and_test(&fp->refcnt)) 906 if (atomic_dec_and_test(&fp->refcnt))
907 kfree(fp); 907 call_rcu_bh(&fp->rcu, sk_filter_rcu_free);
908} 908}
909 909
910static inline void sk_filter_charge(struct sock *sk, struct sk_filter *fp) 910static inline void sk_filter_charge(struct sock *sk, struct sk_filter *fp)