diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2010-06-09 12:33:05 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-06-11 21:37:08 -0400 |
commit | 0c78a92fbd655ab990e2799f645707f05f548e2f (patch) | |
tree | 8dd5bb176c76c142c6b310b4ce4d076d36acaec3 /net/econet/af_econet.c | |
parent | c7de2cf053420d63bac85133469c965d4b1083e1 (diff) |
econet: fix locking
econet lacks proper locking. It holds econet_lock only when inserting or
deleting an entry in econet_sklist, not during lookups.
- convert econet_lock from rwlock to spinlock
- use econet_lock in ec_listening_socket() lookup
- use appropriate sock_hold() / sock_put() to avoid corruptions.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/econet/af_econet.c')
-rw-r--r-- | net/econet/af_econet.c | 27 |
1 files changed, 18 insertions, 9 deletions
diff --git a/net/econet/af_econet.c b/net/econet/af_econet.c index 2a5a8053e000..dc54bd0d083b 100644 --- a/net/econet/af_econet.c +++ b/net/econet/af_econet.c | |||
@@ -48,7 +48,7 @@ | |||
48 | 48 | ||
49 | static const struct proto_ops econet_ops; | 49 | static const struct proto_ops econet_ops; |
50 | static struct hlist_head econet_sklist; | 50 | static struct hlist_head econet_sklist; |
51 | static DEFINE_RWLOCK(econet_lock); | 51 | static DEFINE_SPINLOCK(econet_lock); |
52 | static DEFINE_MUTEX(econet_mutex); | 52 | static DEFINE_MUTEX(econet_mutex); |
53 | 53 | ||
54 | /* Since there are only 256 possible network numbers (or fewer, depends | 54 | /* Since there are only 256 possible network numbers (or fewer, depends |
@@ -98,16 +98,16 @@ struct ec_cb | |||
98 | 98 | ||
99 | static void econet_remove_socket(struct hlist_head *list, struct sock *sk) | 99 | static void econet_remove_socket(struct hlist_head *list, struct sock *sk) |
100 | { | 100 | { |
101 | write_lock_bh(&econet_lock); | 101 | spin_lock_bh(&econet_lock); |
102 | sk_del_node_init(sk); | 102 | sk_del_node_init(sk); |
103 | write_unlock_bh(&econet_lock); | 103 | spin_unlock_bh(&econet_lock); |
104 | } | 104 | } |
105 | 105 | ||
106 | static void econet_insert_socket(struct hlist_head *list, struct sock *sk) | 106 | static void econet_insert_socket(struct hlist_head *list, struct sock *sk) |
107 | { | 107 | { |
108 | write_lock_bh(&econet_lock); | 108 | spin_lock_bh(&econet_lock); |
109 | sk_add_node(sk, list); | 109 | sk_add_node(sk, list); |
110 | write_unlock_bh(&econet_lock); | 110 | spin_unlock_bh(&econet_lock); |
111 | } | 111 | } |
112 | 112 | ||
113 | /* | 113 | /* |
@@ -782,15 +782,19 @@ static struct sock *ec_listening_socket(unsigned char port, unsigned char | |||
782 | struct sock *sk; | 782 | struct sock *sk; |
783 | struct hlist_node *node; | 783 | struct hlist_node *node; |
784 | 784 | ||
785 | spin_lock(&econet_lock); | ||
785 | sk_for_each(sk, node, &econet_sklist) { | 786 | sk_for_each(sk, node, &econet_sklist) { |
786 | struct econet_sock *opt = ec_sk(sk); | 787 | struct econet_sock *opt = ec_sk(sk); |
787 | if ((opt->port == port || opt->port == 0) && | 788 | if ((opt->port == port || opt->port == 0) && |
788 | (opt->station == station || opt->station == 0) && | 789 | (opt->station == station || opt->station == 0) && |
789 | (opt->net == net || opt->net == 0)) | 790 | (opt->net == net || opt->net == 0)) { |
791 | sock_hold(sk); | ||
790 | goto found; | 792 | goto found; |
793 | } | ||
791 | } | 794 | } |
792 | sk = NULL; | 795 | sk = NULL; |
793 | found: | 796 | found: |
797 | spin_unlock(&econet_lock); | ||
794 | return sk; | 798 | return sk; |
795 | } | 799 | } |
796 | 800 | ||
@@ -852,7 +856,7 @@ static void aun_incoming(struct sk_buff *skb, struct aunhdr *ah, size_t len) | |||
852 | { | 856 | { |
853 | struct iphdr *ip = ip_hdr(skb); | 857 | struct iphdr *ip = ip_hdr(skb); |
854 | unsigned char stn = ntohl(ip->saddr) & 0xff; | 858 | unsigned char stn = ntohl(ip->saddr) & 0xff; |
855 | struct sock *sk; | 859 | struct sock *sk = NULL; |
856 | struct sk_buff *newskb; | 860 | struct sk_buff *newskb; |
857 | struct ec_device *edev = skb->dev->ec_ptr; | 861 | struct ec_device *edev = skb->dev->ec_ptr; |
858 | 862 | ||
@@ -882,10 +886,13 @@ static void aun_incoming(struct sk_buff *skb, struct aunhdr *ah, size_t len) | |||
882 | } | 886 | } |
883 | 887 | ||
884 | aun_send_response(ip->saddr, ah->handle, 3, 0); | 888 | aun_send_response(ip->saddr, ah->handle, 3, 0); |
889 | sock_put(sk); | ||
885 | return; | 890 | return; |
886 | 891 | ||
887 | bad: | 892 | bad: |
888 | aun_send_response(ip->saddr, ah->handle, 4, 0); | 893 | aun_send_response(ip->saddr, ah->handle, 4, 0); |
894 | if (sk) | ||
895 | sock_put(sk); | ||
889 | } | 896 | } |
890 | 897 | ||
891 | /* | 898 | /* |
@@ -1050,7 +1057,7 @@ release: | |||
1050 | static int econet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) | 1057 | static int econet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) |
1051 | { | 1058 | { |
1052 | struct ec_framehdr *hdr; | 1059 | struct ec_framehdr *hdr; |
1053 | struct sock *sk; | 1060 | struct sock *sk = NULL; |
1054 | struct ec_device *edev = dev->ec_ptr; | 1061 | struct ec_device *edev = dev->ec_ptr; |
1055 | 1062 | ||
1056 | if (!net_eq(dev_net(dev), &init_net)) | 1063 | if (!net_eq(dev_net(dev), &init_net)) |
@@ -1085,10 +1092,12 @@ static int econet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet | |||
1085 | if (ec_queue_packet(sk, skb, edev->net, hdr->src_stn, hdr->cb, | 1092 | if (ec_queue_packet(sk, skb, edev->net, hdr->src_stn, hdr->cb, |
1086 | hdr->port)) | 1093 | hdr->port)) |
1087 | goto drop; | 1094 | goto drop; |
1088 | 1095 | sock_put(sk); | |
1089 | return NET_RX_SUCCESS; | 1096 | return NET_RX_SUCCESS; |
1090 | 1097 | ||
1091 | drop: | 1098 | drop: |
1099 | if (sk) | ||
1100 | sock_put(sk); | ||
1092 | kfree_skb(skb); | 1101 | kfree_skb(skb); |
1093 | return NET_RX_DROP; | 1102 | return NET_RX_DROP; |
1094 | } | 1103 | } |