Commit 0c78a92f authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller

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: default avatarEric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c7de2cf0
...@@ -48,7 +48,7 @@ ...@@ -48,7 +48,7 @@
static const struct proto_ops econet_ops; static const struct proto_ops econet_ops;
static struct hlist_head econet_sklist; static struct hlist_head econet_sklist;
static DEFINE_RWLOCK(econet_lock); static DEFINE_SPINLOCK(econet_lock);
static DEFINE_MUTEX(econet_mutex); static DEFINE_MUTEX(econet_mutex);
/* Since there are only 256 possible network numbers (or fewer, depends /* Since there are only 256 possible network numbers (or fewer, depends
...@@ -98,16 +98,16 @@ struct ec_cb ...@@ -98,16 +98,16 @@ struct ec_cb
static void econet_remove_socket(struct hlist_head *list, struct sock *sk) static void econet_remove_socket(struct hlist_head *list, struct sock *sk)
{ {
write_lock_bh(&econet_lock); spin_lock_bh(&econet_lock);
sk_del_node_init(sk); sk_del_node_init(sk);
write_unlock_bh(&econet_lock); spin_unlock_bh(&econet_lock);
} }
static void econet_insert_socket(struct hlist_head *list, struct sock *sk) static void econet_insert_socket(struct hlist_head *list, struct sock *sk)
{ {
write_lock_bh(&econet_lock); spin_lock_bh(&econet_lock);
sk_add_node(sk, list); sk_add_node(sk, list);
write_unlock_bh(&econet_lock); spin_unlock_bh(&econet_lock);
} }
/* /*
...@@ -782,15 +782,19 @@ static struct sock *ec_listening_socket(unsigned char port, unsigned char ...@@ -782,15 +782,19 @@ static struct sock *ec_listening_socket(unsigned char port, unsigned char
struct sock *sk; struct sock *sk;
struct hlist_node *node; struct hlist_node *node;
spin_lock(&econet_lock);
sk_for_each(sk, node, &econet_sklist) { sk_for_each(sk, node, &econet_sklist) {
struct econet_sock *opt = ec_sk(sk); struct econet_sock *opt = ec_sk(sk);
if ((opt->port == port || opt->port == 0) && if ((opt->port == port || opt->port == 0) &&
(opt->station == station || opt->station == 0) && (opt->station == station || opt->station == 0) &&
(opt->net == net || opt->net == 0)) (opt->net == net || opt->net == 0)) {
sock_hold(sk);
goto found; goto found;
} }
}
sk = NULL; sk = NULL;
found: found:
spin_unlock(&econet_lock);
return sk; return sk;
} }
...@@ -852,7 +856,7 @@ static void aun_incoming(struct sk_buff *skb, struct aunhdr *ah, size_t len) ...@@ -852,7 +856,7 @@ static void aun_incoming(struct sk_buff *skb, struct aunhdr *ah, size_t len)
{ {
struct iphdr *ip = ip_hdr(skb); struct iphdr *ip = ip_hdr(skb);
unsigned char stn = ntohl(ip->saddr) & 0xff; unsigned char stn = ntohl(ip->saddr) & 0xff;
struct sock *sk; struct sock *sk = NULL;
struct sk_buff *newskb; struct sk_buff *newskb;
struct ec_device *edev = skb->dev->ec_ptr; struct ec_device *edev = skb->dev->ec_ptr;
...@@ -882,10 +886,13 @@ static void aun_incoming(struct sk_buff *skb, struct aunhdr *ah, size_t len) ...@@ -882,10 +886,13 @@ static void aun_incoming(struct sk_buff *skb, struct aunhdr *ah, size_t len)
} }
aun_send_response(ip->saddr, ah->handle, 3, 0); aun_send_response(ip->saddr, ah->handle, 3, 0);
sock_put(sk);
return; return;
bad: bad:
aun_send_response(ip->saddr, ah->handle, 4, 0); aun_send_response(ip->saddr, ah->handle, 4, 0);
if (sk)
sock_put(sk);
} }
/* /*
...@@ -1050,7 +1057,7 @@ static int __init aun_udp_initialise(void) ...@@ -1050,7 +1057,7 @@ static int __init aun_udp_initialise(void)
static int econet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) static int econet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
{ {
struct ec_framehdr *hdr; struct ec_framehdr *hdr;
struct sock *sk; struct sock *sk = NULL;
struct ec_device *edev = dev->ec_ptr; struct ec_device *edev = dev->ec_ptr;
if (!net_eq(dev_net(dev), &init_net)) 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,10 +1092,12 @@ static int econet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet
if (ec_queue_packet(sk, skb, edev->net, hdr->src_stn, hdr->cb, if (ec_queue_packet(sk, skb, edev->net, hdr->src_stn, hdr->cb,
hdr->port)) hdr->port))
goto drop; goto drop;
sock_put(sk);
return NET_RX_SUCCESS; return NET_RX_SUCCESS;
drop: drop:
if (sk)
sock_put(sk);
kfree_skb(skb); kfree_skb(skb);
return NET_RX_DROP; return NET_RX_DROP;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment