Skip to content

Commit 77146b5

Browse files
committed
Merge branch 'l2tp-tunnel-refs'
Guillaume Nault says: ==================== l2tp: fix some l2tp_tunnel_find() issues in l2tp_netlink Since l2tp_tunnel_find() doesn't take a reference on the tunnel it returns, its users are almost guaranteed to be racy. This series defines l2tp_tunnel_get() which can be used as a safe replacement, and converts some of l2tp_tunnel_find() users in the l2tp_netlink module. Other users often combine this issue with other more or less subtle races. They will be fixed incrementally in followup series. ==================== Signed-off-by: David S. Miller <[email protected]>
2 parents 9ee369a + e702c12 commit 77146b5

File tree

3 files changed

+73
-72
lines changed

3 files changed

+73
-72
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: 39 additions & 27 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;
@@ -271,8 +273,8 @@ static int l2tp_nl_cmd_tunnel_delete(struct sk_buff *skb, struct genl_info *info
271273
}
272274
tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
273275

274-
tunnel = l2tp_tunnel_find(net, tunnel_id);
275-
if (tunnel == NULL) {
276+
tunnel = l2tp_tunnel_get(net, tunnel_id);
277+
if (!tunnel) {
276278
ret = -ENODEV;
277279
goto out;
278280
}
@@ -282,6 +284,8 @@ static int l2tp_nl_cmd_tunnel_delete(struct sk_buff *skb, struct genl_info *info
282284

283285
(void) l2tp_tunnel_delete(tunnel);
284286

287+
l2tp_tunnel_dec_refcount(tunnel);
288+
285289
out:
286290
return ret;
287291
}
@@ -299,8 +303,8 @@ static int l2tp_nl_cmd_tunnel_modify(struct sk_buff *skb, struct genl_info *info
299303
}
300304
tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
301305

302-
tunnel = l2tp_tunnel_find(net, tunnel_id);
303-
if (tunnel == NULL) {
306+
tunnel = l2tp_tunnel_get(net, tunnel_id);
307+
if (!tunnel) {
304308
ret = -ENODEV;
305309
goto out;
306310
}
@@ -311,6 +315,8 @@ static int l2tp_nl_cmd_tunnel_modify(struct sk_buff *skb, struct genl_info *info
311315
ret = l2tp_tunnel_notify(&l2tp_nl_family, info,
312316
tunnel, L2TP_CMD_TUNNEL_MODIFY);
313317

318+
l2tp_tunnel_dec_refcount(tunnel);
319+
314320
out:
315321
return ret;
316322
}
@@ -438,34 +444,37 @@ static int l2tp_nl_cmd_tunnel_get(struct sk_buff *skb, struct genl_info *info)
438444

439445
if (!info->attrs[L2TP_ATTR_CONN_ID]) {
440446
ret = -EINVAL;
441-
goto out;
447+
goto err;
442448
}
443449

444450
tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
445451

446-
tunnel = l2tp_tunnel_find(net, tunnel_id);
447-
if (tunnel == NULL) {
448-
ret = -ENODEV;
449-
goto out;
450-
}
451-
452452
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
453453
if (!msg) {
454454
ret = -ENOMEM;
455-
goto out;
455+
goto err;
456+
}
457+
458+
tunnel = l2tp_tunnel_get(net, tunnel_id);
459+
if (!tunnel) {
460+
ret = -ENODEV;
461+
goto err_nlmsg;
456462
}
457463

458464
ret = l2tp_nl_tunnel_send(msg, info->snd_portid, info->snd_seq,
459465
NLM_F_ACK, tunnel, L2TP_CMD_TUNNEL_GET);
460466
if (ret < 0)
461-
goto err_out;
467+
goto err_nlmsg_tunnel;
468+
469+
l2tp_tunnel_dec_refcount(tunnel);
462470

463471
return genlmsg_unicast(net, msg, info->snd_portid);
464472

465-
err_out:
473+
err_nlmsg_tunnel:
474+
l2tp_tunnel_dec_refcount(tunnel);
475+
err_nlmsg:
466476
nlmsg_free(msg);
467-
468-
out:
477+
err:
469478
return ret;
470479
}
471480

@@ -509,33 +518,34 @@ static int l2tp_nl_cmd_session_create(struct sk_buff *skb, struct genl_info *inf
509518
ret = -EINVAL;
510519
goto out;
511520
}
521+
512522
tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
513-
tunnel = l2tp_tunnel_find(net, tunnel_id);
523+
tunnel = l2tp_tunnel_get(net, tunnel_id);
514524
if (!tunnel) {
515525
ret = -ENODEV;
516526
goto out;
517527
}
518528

519529
if (!info->attrs[L2TP_ATTR_SESSION_ID]) {
520530
ret = -EINVAL;
521-
goto out;
531+
goto out_tunnel;
522532
}
523533
session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]);
524534

525535
if (!info->attrs[L2TP_ATTR_PEER_SESSION_ID]) {
526536
ret = -EINVAL;
527-
goto out;
537+
goto out_tunnel;
528538
}
529539
peer_session_id = nla_get_u32(info->attrs[L2TP_ATTR_PEER_SESSION_ID]);
530540

531541
if (!info->attrs[L2TP_ATTR_PW_TYPE]) {
532542
ret = -EINVAL;
533-
goto out;
543+
goto out_tunnel;
534544
}
535545
cfg.pw_type = nla_get_u16(info->attrs[L2TP_ATTR_PW_TYPE]);
536546
if (cfg.pw_type >= __L2TP_PWTYPE_MAX) {
537547
ret = -EINVAL;
538-
goto out;
548+
goto out_tunnel;
539549
}
540550

541551
if (tunnel->version > 2) {
@@ -557,7 +567,7 @@ static int l2tp_nl_cmd_session_create(struct sk_buff *skb, struct genl_info *inf
557567
u16 len = nla_len(info->attrs[L2TP_ATTR_COOKIE]);
558568
if (len > 8) {
559569
ret = -EINVAL;
560-
goto out;
570+
goto out_tunnel;
561571
}
562572
cfg.cookie_len = len;
563573
memcpy(&cfg.cookie[0], nla_data(info->attrs[L2TP_ATTR_COOKIE]), len);
@@ -566,7 +576,7 @@ static int l2tp_nl_cmd_session_create(struct sk_buff *skb, struct genl_info *inf
566576
u16 len = nla_len(info->attrs[L2TP_ATTR_PEER_COOKIE]);
567577
if (len > 8) {
568578
ret = -EINVAL;
569-
goto out;
579+
goto out_tunnel;
570580
}
571581
cfg.peer_cookie_len = len;
572582
memcpy(&cfg.peer_cookie[0], nla_data(info->attrs[L2TP_ATTR_PEER_COOKIE]), len);
@@ -609,7 +619,7 @@ static int l2tp_nl_cmd_session_create(struct sk_buff *skb, struct genl_info *inf
609619
if ((l2tp_nl_cmd_ops[cfg.pw_type] == NULL) ||
610620
(l2tp_nl_cmd_ops[cfg.pw_type]->session_create == NULL)) {
611621
ret = -EPROTONOSUPPORT;
612-
goto out;
622+
goto out_tunnel;
613623
}
614624

615625
/* Check that pseudowire-specific params are present */
@@ -619,7 +629,7 @@ static int l2tp_nl_cmd_session_create(struct sk_buff *skb, struct genl_info *inf
619629
case L2TP_PWTYPE_ETH_VLAN:
620630
if (!info->attrs[L2TP_ATTR_VLAN_ID]) {
621631
ret = -EINVAL;
622-
goto out;
632+
goto out_tunnel;
623633
}
624634
break;
625635
case L2TP_PWTYPE_ETH:
@@ -647,6 +657,8 @@ static int l2tp_nl_cmd_session_create(struct sk_buff *skb, struct genl_info *inf
647657
}
648658
}
649659

660+
out_tunnel:
661+
l2tp_tunnel_dec_refcount(tunnel);
650662
out:
651663
return ret;
652664
}

0 commit comments

Comments
 (0)