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

Commit 5846c131 authored by Guillaume Nault's avatar Guillaume Nault Committed by David S. Miller
Browse files

l2tp: hold reference on tunnels in netlink dumps



l2tp_tunnel_find_nth() is unsafe: no reference is held on the returned
tunnel, therefore it can be freed whenever the caller uses it.
This patch defines l2tp_tunnel_get_nth() which works similarly, but
also takes a reference on the returned tunnel. The caller then has to
drop it after it stops using the tunnel.

Convert netlink dumps to make them safe against concurrent tunnel
deletion.

Fixes: 309795f4 ("l2tp: Add netlink control API for L2TP")
Signed-off-by: default avatarGuillaume Nault <g.nault@alphalink.fr>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 9267c430
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -183,6 +183,26 @@ struct l2tp_tunnel *l2tp_tunnel_get(const struct net *net, u32 tunnel_id)
}
EXPORT_SYMBOL_GPL(l2tp_tunnel_get);

struct l2tp_tunnel *l2tp_tunnel_get_nth(const struct net *net, int nth)
{
	const struct l2tp_net *pn = l2tp_pernet(net);
	struct l2tp_tunnel *tunnel;
	int count = 0;

	rcu_read_lock_bh();
	list_for_each_entry_rcu(tunnel, &pn->l2tp_tunnel_list, list) {
		if (++count > nth) {
			l2tp_tunnel_inc_refcount(tunnel);
			rcu_read_unlock_bh();
			return tunnel;
		}
	}
	rcu_read_unlock_bh();

	return NULL;
}
EXPORT_SYMBOL_GPL(l2tp_tunnel_get_nth);

/* Lookup a session. A new reference is held on the returned session. */
struct l2tp_session *l2tp_session_get(const struct net *net,
				      struct l2tp_tunnel *tunnel,
+2 −0
Original line number Diff line number Diff line
@@ -212,6 +212,8 @@ static inline void *l2tp_session_priv(struct l2tp_session *session)
}

struct l2tp_tunnel *l2tp_tunnel_get(const struct net *net, u32 tunnel_id);
struct l2tp_tunnel *l2tp_tunnel_get_nth(const struct net *net, int nth);

void l2tp_tunnel_free(struct l2tp_tunnel *tunnel);

struct l2tp_session *l2tp_session_get(const struct net *net,
+8 −3
Original line number Diff line number Diff line
@@ -487,14 +487,17 @@ static int l2tp_nl_cmd_tunnel_dump(struct sk_buff *skb, struct netlink_callback
	struct net *net = sock_net(skb->sk);

	for (;;) {
		tunnel = l2tp_tunnel_find_nth(net, ti);
		tunnel = l2tp_tunnel_get_nth(net, ti);
		if (tunnel == NULL)
			goto out;

		if (l2tp_nl_tunnel_send(skb, NETLINK_CB(cb->skb).portid,
					cb->nlh->nlmsg_seq, NLM_F_MULTI,
					tunnel, L2TP_CMD_TUNNEL_GET) < 0)
					tunnel, L2TP_CMD_TUNNEL_GET) < 0) {
			l2tp_tunnel_dec_refcount(tunnel);
			goto out;
		}
		l2tp_tunnel_dec_refcount(tunnel);

		ti++;
	}
@@ -848,7 +851,7 @@ static int l2tp_nl_cmd_session_dump(struct sk_buff *skb, struct netlink_callback

	for (;;) {
		if (tunnel == NULL) {
			tunnel = l2tp_tunnel_find_nth(net, ti);
			tunnel = l2tp_tunnel_get_nth(net, ti);
			if (tunnel == NULL)
				goto out;
		}
@@ -856,6 +859,7 @@ static int l2tp_nl_cmd_session_dump(struct sk_buff *skb, struct netlink_callback
		session = l2tp_session_get_nth(tunnel, si);
		if (session == NULL) {
			ti++;
			l2tp_tunnel_dec_refcount(tunnel);
			tunnel = NULL;
			si = 0;
			continue;
@@ -865,6 +869,7 @@ static int l2tp_nl_cmd_session_dump(struct sk_buff *skb, struct netlink_callback
					 cb->nlh->nlmsg_seq, NLM_F_MULTI,
					 session, L2TP_CMD_SESSION_GET) < 0) {
			l2tp_session_dec_refcount(session);
			l2tp_tunnel_dec_refcount(tunnel);
			break;
		}
		l2tp_session_dec_refcount(session);