Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+18 −9
Original line number Diff line number Diff line
@@ -48,7 +48,7 @@

static const struct proto_ops econet_ops;
static struct hlist_head econet_sklist;
static DEFINE_RWLOCK(econet_lock);
static DEFINE_SPINLOCK(econet_lock);
static DEFINE_MUTEX(econet_mutex);

/* Since there are only 256 possible network numbers (or fewer, depends
@@ -98,16 +98,16 @@ struct ec_cb

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);
	write_unlock_bh(&econet_lock);
	spin_unlock_bh(&econet_lock);
}

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);
	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
	struct sock *sk;
	struct hlist_node *node;

	spin_lock(&econet_lock);
	sk_for_each(sk, node, &econet_sklist) {
		struct econet_sock *opt = ec_sk(sk);
		if ((opt->port == port || opt->port == 0) &&
		    (opt->station == station || opt->station == 0) &&
		    (opt->net == net || opt->net == 0))
		    (opt->net == net || opt->net == 0)) {
			sock_hold(sk);
			goto found;
		}
	}
	sk = NULL;
found:
	spin_unlock(&econet_lock);
	return sk;
}

@@ -852,7 +856,7 @@ static void aun_incoming(struct sk_buff *skb, struct aunhdr *ah, size_t len)
{
	struct iphdr *ip = ip_hdr(skb);
	unsigned char stn = ntohl(ip->saddr) & 0xff;
	struct sock *sk;
	struct sock *sk = NULL;
	struct sk_buff *newskb;
	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)
	}

	aun_send_response(ip->saddr, ah->handle, 3, 0);
	sock_put(sk);
	return;

bad:
	aun_send_response(ip->saddr, ah->handle, 4, 0);
	if (sk)
		sock_put(sk);
}

/*
@@ -1050,7 +1057,7 @@ release:
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 sock *sk;
	struct sock *sk = NULL;
	struct ec_device *edev = dev->ec_ptr;

	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
	if (ec_queue_packet(sk, skb, edev->net, hdr->src_stn, hdr->cb,
			    hdr->port))
		goto drop;

	sock_put(sk);
	return NET_RX_SUCCESS;

drop:
	if (sk)
		sock_put(sk);
	kfree_skb(skb);
	return NET_RX_DROP;
}