diff options
author | Dmitry Mishin <dim@openvz.org> | 2006-08-31 18:28:39 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-09-22 18:18:47 -0400 |
commit | fda9ef5d679b07c9d9097aaf6ef7f069d794a8f9 (patch) | |
tree | 6a265dc2038bc5568c5a499e6c8d4733650ed3f7 /include | |
parent | dc435e6dac1439340eaeceef84022c4e4749796d (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.h | 13 | ||||
-rw-r--r-- | include/net/sock.h | 34 |
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 | ||
26 | struct sock_filter /* Filter block */ | 26 | struct 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 | ||
34 | struct sock_fprog /* Required for SO_ATTACH_FILTER. */ | 34 | struct sock_fprog /* Required for SO_ATTACH_FILTER. */ |
@@ -41,8 +41,9 @@ struct sock_fprog /* Required for SO_ATTACH_FILTER. */ | |||
41 | struct sk_filter | 41 | struct 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 | ||
48 | static inline unsigned int sk_filter_len(struct sk_filter *fp) | 49 | static 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 | ||
865 | static inline int sk_filter(struct sock *sk, struct sk_buff *skb, int needlock) | 865 | static 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 | ||
894 | static 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 | |||
900 | static inline void sk_filter_release(struct sock *sk, struct sk_filter *fp) | 900 | static 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 | ||
910 | static inline void sk_filter_charge(struct sock *sk, struct sk_filter *fp) | 910 | static inline void sk_filter_charge(struct sock *sk, struct sk_filter *fp) |