| From: Guillaume Nault <g.nault@alphalink.fr> |
| Date: Thu, 12 Apr 2018 20:50:33 +0200 |
| Subject: l2tp: hold reference on tunnels in netlink dumps |
| |
| commit 5846c131c39b6d0add36ec19dc8650700690f930 upstream. |
| |
| 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: 309795f4bec2 ("l2tp: Add netlink control API for L2TP") |
| Signed-off-by: Guillaume Nault <g.nault@alphalink.fr> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| [bwh: Backported to 3.16: adjust context] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| net/l2tp/l2tp_core.c | 20 ++++++++++++++++++++ |
| net/l2tp/l2tp_core.h | 2 ++ |
| net/l2tp/l2tp_netlink.c | 11 ++++++++--- |
| 3 files changed, 30 insertions(+), 3 deletions(-) |
| |
| --- a/net/l2tp/l2tp_core.c |
| +++ b/net/l2tp/l2tp_core.c |
| @@ -231,6 +231,26 @@ struct l2tp_tunnel *l2tp_tunnel_get(cons |
| } |
| 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); |
| + |
| /* Like l2tp_session_find() but takes a reference on the returned session. |
| * Optionally calls session->ref() too if do_ref is true. |
| */ |
| --- a/net/l2tp/l2tp_core.h |
| +++ b/net/l2tp/l2tp_core.h |
| @@ -227,6 +227,8 @@ static inline void *l2tp_session_priv(st |
| } |
| |
| 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, |
| --- a/net/l2tp/l2tp_netlink.c |
| +++ b/net/l2tp/l2tp_netlink.c |
| @@ -395,14 +395,17 @@ static int l2tp_nl_cmd_tunnel_dump(struc |
| 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) <= 0) |
| + tunnel) <= 0) { |
| + l2tp_tunnel_dec_refcount(tunnel); |
| goto out; |
| + } |
| + l2tp_tunnel_dec_refcount(tunnel); |
| |
| ti++; |
| } |
| @@ -746,7 +749,7 @@ static int l2tp_nl_cmd_session_dump(stru |
| |
| for (;;) { |
| if (tunnel == NULL) { |
| - tunnel = l2tp_tunnel_find_nth(net, ti); |
| + tunnel = l2tp_tunnel_get_nth(net, ti); |
| if (tunnel == NULL) |
| goto out; |
| } |
| @@ -754,6 +757,7 @@ static int l2tp_nl_cmd_session_dump(stru |
| session = l2tp_session_get_nth(tunnel, si, false); |
| if (session == NULL) { |
| ti++; |
| + l2tp_tunnel_dec_refcount(tunnel); |
| tunnel = NULL; |
| si = 0; |
| continue; |
| @@ -763,6 +767,7 @@ static int l2tp_nl_cmd_session_dump(stru |
| cb->nlh->nlmsg_seq, NLM_F_MULTI, |
| session) <= 0) { |
| l2tp_session_dec_refcount(session); |
| + l2tp_tunnel_dec_refcount(tunnel); |
| break; |
| } |
| l2tp_session_dec_refcount(session); |