Skip to content

Commit 54652eb

Browse files
Guillaume Naultdavem330
authored andcommitted
l2tp: hold tunnel while looking up sessions in l2tp_netlink
l2tp_tunnel_find() doesn't take a reference on the returned tunnel. Therefore, it's unsafe to use it because the returned tunnel can go away on us anytime. Fix this by defining l2tp_tunnel_get(), which works like l2tp_tunnel_find(), but takes a reference on the returned tunnel. Caller then has to drop this reference using l2tp_tunnel_dec_refcount(). As l2tp_tunnel_dec_refcount() needs to be moved to l2tp_core.h, let's simplify the patch and not move the L2TP_REFCNT_DEBUG part. This code has been broken (not even compiling) in May 2012 by commit a4ca44f ("net: l2tp: Standardize logging styles") and fixed more than two years later by commit 29abe2f ("l2tp: fix missing line continuation"). So it doesn't appear to be used by anyone. Same thing for l2tp_tunnel_free(); instead of moving it to l2tp_core.h, let's just simplify things and call kfree_rcu() directly in l2tp_tunnel_dec_refcount(). Extra assertions and debugging code provided by l2tp_tunnel_free() didn't help catching any of the reference counting and socket handling issues found while working on this series. Fixes: 309795f ("l2tp: Add netlink control API for L2TP") Signed-off-by: Guillaume Nault <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 9ee369a commit 54652eb

File tree

3 files changed

+38
-47
lines changed

3 files changed

+38
-47
lines changed

net/l2tp/l2tp_core.c

Lines changed: 21 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,6 @@ struct l2tp_net {
113113
spinlock_t l2tp_session_hlist_lock;
114114
};
115115

116-
static void l2tp_tunnel_free(struct l2tp_tunnel *tunnel);
117116

118117
static inline struct l2tp_tunnel *l2tp_tunnel(struct sock *sk)
119118
{
@@ -127,39 +126,6 @@ static inline struct l2tp_net *l2tp_pernet(const struct net *net)
127126
return net_generic(net, l2tp_net_id);
128127
}
129128

130-
/* Tunnel reference counts. Incremented per session that is added to
131-
* the tunnel.
132-
*/
133-
static inline void l2tp_tunnel_inc_refcount_1(struct l2tp_tunnel *tunnel)
134-
{
135-
refcount_inc(&tunnel->ref_count);
136-
}
137-
138-
static inline void l2tp_tunnel_dec_refcount_1(struct l2tp_tunnel *tunnel)
139-
{
140-
if (refcount_dec_and_test(&tunnel->ref_count))
141-
l2tp_tunnel_free(tunnel);
142-
}
143-
#ifdef L2TP_REFCNT_DEBUG
144-
#define l2tp_tunnel_inc_refcount(_t) \
145-
do { \
146-
pr_debug("l2tp_tunnel_inc_refcount: %s:%d %s: cnt=%d\n", \
147-
__func__, __LINE__, (_t)->name, \
148-
refcount_read(&_t->ref_count)); \
149-
l2tp_tunnel_inc_refcount_1(_t); \
150-
} while (0)
151-
#define l2tp_tunnel_dec_refcount(_t) \
152-
do { \
153-
pr_debug("l2tp_tunnel_dec_refcount: %s:%d %s: cnt=%d\n", \
154-
__func__, __LINE__, (_t)->name, \
155-
refcount_read(&_t->ref_count)); \
156-
l2tp_tunnel_dec_refcount_1(_t); \
157-
} while (0)
158-
#else
159-
#define l2tp_tunnel_inc_refcount(t) l2tp_tunnel_inc_refcount_1(t)
160-
#define l2tp_tunnel_dec_refcount(t) l2tp_tunnel_dec_refcount_1(t)
161-
#endif
162-
163129
/* Session hash global list for L2TPv3.
164130
* The session_id SHOULD be random according to RFC3931, but several
165131
* L2TP implementations use incrementing session_ids. So we do a real
@@ -229,6 +195,27 @@ l2tp_session_id_hash(struct l2tp_tunnel *tunnel, u32 session_id)
229195
return &tunnel->session_hlist[hash_32(session_id, L2TP_HASH_BITS)];
230196
}
231197

198+
/* Lookup a tunnel. A new reference is held on the returned tunnel. */
199+
struct l2tp_tunnel *l2tp_tunnel_get(const struct net *net, u32 tunnel_id)
200+
{
201+
const struct l2tp_net *pn = l2tp_pernet(net);
202+
struct l2tp_tunnel *tunnel;
203+
204+
rcu_read_lock_bh();
205+
list_for_each_entry_rcu(tunnel, &pn->l2tp_tunnel_list, list) {
206+
if (tunnel->tunnel_id == tunnel_id) {
207+
l2tp_tunnel_inc_refcount(tunnel);
208+
rcu_read_unlock_bh();
209+
210+
return tunnel;
211+
}
212+
}
213+
rcu_read_unlock_bh();
214+
215+
return NULL;
216+
}
217+
EXPORT_SYMBOL_GPL(l2tp_tunnel_get);
218+
232219
/* Lookup a session. A new reference is held on the returned session.
233220
* Optionally calls session->ref() too if do_ref is true.
234221
*/
@@ -1348,17 +1335,6 @@ static void l2tp_udp_encap_destroy(struct sock *sk)
13481335
}
13491336
}
13501337

1351-
/* Really kill the tunnel.
1352-
* Come here only when all sessions have been cleared from the tunnel.
1353-
*/
1354-
static void l2tp_tunnel_free(struct l2tp_tunnel *tunnel)
1355-
{
1356-
BUG_ON(refcount_read(&tunnel->ref_count) != 0);
1357-
BUG_ON(tunnel->sock != NULL);
1358-
l2tp_info(tunnel, L2TP_MSG_CONTROL, "%s: free...\n", tunnel->name);
1359-
kfree_rcu(tunnel, rcu);
1360-
}
1361-
13621338
/* Workqueue tunnel deletion function */
13631339
static void l2tp_tunnel_del_work(struct work_struct *work)
13641340
{

net/l2tp/l2tp_core.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,8 @@ static inline struct l2tp_tunnel *l2tp_sock_to_tunnel(struct sock *sk)
231231
return tunnel;
232232
}
233233

234+
struct l2tp_tunnel *l2tp_tunnel_get(const struct net *net, u32 tunnel_id);
235+
234236
struct l2tp_session *l2tp_session_get(const struct net *net,
235237
struct l2tp_tunnel *tunnel,
236238
u32 session_id, bool do_ref);
@@ -269,6 +271,17 @@ int l2tp_nl_register_ops(enum l2tp_pwtype pw_type,
269271
void l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type);
270272
int l2tp_ioctl(struct sock *sk, int cmd, unsigned long arg);
271273

274+
static inline void l2tp_tunnel_inc_refcount(struct l2tp_tunnel *tunnel)
275+
{
276+
refcount_inc(&tunnel->ref_count);
277+
}
278+
279+
static inline void l2tp_tunnel_dec_refcount(struct l2tp_tunnel *tunnel)
280+
{
281+
if (refcount_dec_and_test(&tunnel->ref_count))
282+
kfree_rcu(tunnel, rcu);
283+
}
284+
272285
/* Session reference counts. Incremented when code obtains a reference
273286
* to a session.
274287
*/

net/l2tp/l2tp_netlink.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,12 @@ static struct l2tp_session *l2tp_nl_session_get(struct genl_info *info,
6565
(info->attrs[L2TP_ATTR_CONN_ID])) {
6666
tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
6767
session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]);
68-
tunnel = l2tp_tunnel_find(net, tunnel_id);
69-
if (tunnel)
68+
tunnel = l2tp_tunnel_get(net, tunnel_id);
69+
if (tunnel) {
7070
session = l2tp_session_get(net, tunnel, session_id,
7171
do_ref);
72+
l2tp_tunnel_dec_refcount(tunnel);
73+
}
7274
}
7375

7476
return session;

0 commit comments

Comments
 (0)