Skip to content

Commit 18bdaef

Browse files
j-c-hgregkh
authored andcommitted
l2tp: fix races with tunnel socket close
[ Upstream commit d00fa9a ] The tunnel socket tunnel->sock (struct sock) is accessed when preparing a new ppp session on a tunnel at pppol2tp_session_init. If the socket is closed by a thread while another is creating a new session, the threads race. In pppol2tp_connect, the tunnel object may be created if the pppol2tp socket is associated with the special session_id 0 and the tunnel socket is looked up using the provided fd. When handling this, pppol2tp_connect cannot sock_hold the tunnel socket to prevent it being destroyed during pppol2tp_connect since this may itself may race with the socket being destroyed. Doing sockfd_lookup in pppol2tp_connect isn't sufficient to prevent tunnel->sock going away either because a given tunnel socket fd may be reused between calls to pppol2tp_connect. Instead, have l2tp_tunnel_create sock_hold the tunnel socket before it does sockfd_put. This ensures that the tunnel's socket is always extant while the tunnel object exists. Hold a ref on the socket until the tunnel is destroyed and ensure that all tunnel destroy paths go through a common function (l2tp_tunnel_delete) since this will do the final sock_put to release the tunnel socket. Since the tunnel's socket is now guaranteed to exist if the tunnel exists, we no longer need to use sockfd_lookup via l2tp_sock_to_tunnel to derive the tunnel from the socket since this is always sk_user_data. Also, sessions no longer sock_hold the tunnel socket since sessions already hold a tunnel ref and the tunnel sock will not be freed until the tunnel is freed. Removing these sock_holds in l2tp_session_register avoids a possible sock leak in the pppol2tp_connect error path if l2tp_session_register succeeds but attaching a ppp channel fails. The pppol2tp_connect error path could have been fixed instead and have the sock ref dropped when the session is freed, but doing a sock_put of the tunnel socket when the session is freed would require a new session_free callback. It is simpler to just remove the sock_hold of the tunnel socket in l2tp_session_register, now that the tunnel socket lifetime is guaranteed. Finally, some init code in l2tp_tunnel_create is reordered to ensure that the new tunnel object's refcount is set and the tunnel socket ref is taken before the tunnel socket destructor callbacks are set. kasan: CONFIG_KASAN_INLINE enabled kasan: GPF could be caused by NULL-ptr deref or user memory access general protection fault: 0000 [#1] SMP KASAN Modules linked in: CPU: 0 PID: 4360 Comm: syzbot_19c09769 Not tainted 4.16.0-rc2+ #34 Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006 RIP: 0010:pppol2tp_session_init+0x1d6/0x500 RSP: 0018:ffff88001377fb40 EFLAGS: 00010212 RAX: dffffc0000000000 RBX: ffff88001636a940 RCX: ffffffff84836c1d RDX: 0000000000000045 RSI: 0000000055976744 RDI: 0000000000000228 RBP: ffff88001377fb60 R08: ffffffff84836bc8 R09: 0000000000000002 R10: ffff88001377fab8 R11: 0000000000000001 R12: 0000000000000000 R13: ffff88001636aac8 R14: ffff8800160f81c0 R15: 1ffff100026eff76 FS: 00007ffb3ea66700(0000) GS:ffff88001a400000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000020e77000 CR3: 0000000016261000 CR4: 00000000000006f0 Call Trace: pppol2tp_connect+0xd18/0x13c0 ? pppol2tp_session_create+0x170/0x170 ? __might_fault+0x115/0x1d0 ? lock_downgrade+0x860/0x860 ? __might_fault+0xe5/0x1d0 ? security_socket_connect+0x8e/0xc0 SYSC_connect+0x1b6/0x310 ? SYSC_bind+0x280/0x280 ? __do_page_fault+0x5d1/0xca0 ? up_read+0x1f/0x40 ? __do_page_fault+0x3c8/0xca0 SyS_connect+0x29/0x30 ? SyS_accept+0x40/0x40 do_syscall_64+0x1e0/0x730 ? trace_hardirqs_off_thunk+0x1a/0x1c entry_SYSCALL_64_after_hwframe+0x42/0xb7 RIP: 0033:0x7ffb3e376259 RSP: 002b:00007ffeda4f6508 EFLAGS: 00000202 ORIG_RAX: 000000000000002a RAX: ffffffffffffffda RBX: 0000000020e77012 RCX: 00007ffb3e376259 RDX: 000000000000002e RSI: 0000000020e77000 RDI: 0000000000000004 RBP: 00007ffeda4f6540 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000202 R12: 0000000000400b60 R13: 00007ffeda4f6660 R14: 0000000000000000 R15: 0000000000000000 Code: 80 3d b0 ff 06 02 00 0f 84 07 02 00 00 e8 13 d6 db fc 49 8d bc 24 28 02 00 00 48 b8 00 00 00 00 00 fc ff df 48 89 f a 48 c1 ea 03 <80> 3c 02 00 0f 85 ed 02 00 00 4d 8b a4 24 28 02 00 00 e8 13 16 Fixes: 80d84ef ("l2tp: prevent l2tp_tunnel_delete racing with userspace close") Signed-off-by: James Chapman <[email protected]> Signed-off-by: David S. Miller <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 8a319b5 commit 18bdaef

File tree

4 files changed

+42
-116
lines changed

4 files changed

+42
-116
lines changed

net/l2tp/l2tp_core.c

Lines changed: 34 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -136,51 +136,6 @@ l2tp_session_id_hash_2(struct l2tp_net *pn, u32 session_id)
136136

137137
}
138138

139-
/* Lookup the tunnel socket, possibly involving the fs code if the socket is
140-
* owned by userspace. A struct sock returned from this function must be
141-
* released using l2tp_tunnel_sock_put once you're done with it.
142-
*/
143-
static struct sock *l2tp_tunnel_sock_lookup(struct l2tp_tunnel *tunnel)
144-
{
145-
int err = 0;
146-
struct socket *sock = NULL;
147-
struct sock *sk = NULL;
148-
149-
if (!tunnel)
150-
goto out;
151-
152-
if (tunnel->fd >= 0) {
153-
/* Socket is owned by userspace, who might be in the process
154-
* of closing it. Look the socket up using the fd to ensure
155-
* consistency.
156-
*/
157-
sock = sockfd_lookup(tunnel->fd, &err);
158-
if (sock)
159-
sk = sock->sk;
160-
} else {
161-
/* Socket is owned by kernelspace */
162-
sk = tunnel->sock;
163-
sock_hold(sk);
164-
}
165-
166-
out:
167-
return sk;
168-
}
169-
170-
/* Drop a reference to a tunnel socket obtained via. l2tp_tunnel_sock_put */
171-
static void l2tp_tunnel_sock_put(struct sock *sk)
172-
{
173-
struct l2tp_tunnel *tunnel = l2tp_sock_to_tunnel(sk);
174-
if (tunnel) {
175-
if (tunnel->fd >= 0) {
176-
/* Socket is owned by userspace */
177-
sockfd_put(sk->sk_socket);
178-
}
179-
sock_put(sk);
180-
}
181-
sock_put(sk);
182-
}
183-
184139
/* Session hash list.
185140
* The session_id SHOULD be random according to RFC2661, but several
186141
* L2TP implementations (Cisco and Microsoft) use incrementing
@@ -193,6 +148,13 @@ l2tp_session_id_hash(struct l2tp_tunnel *tunnel, u32 session_id)
193148
return &tunnel->session_hlist[hash_32(session_id, L2TP_HASH_BITS)];
194149
}
195150

151+
void l2tp_tunnel_free(struct l2tp_tunnel *tunnel)
152+
{
153+
sock_put(tunnel->sock);
154+
/* the tunnel is freed in the socket destructor */
155+
}
156+
EXPORT_SYMBOL(l2tp_tunnel_free);
157+
196158
/* Lookup a tunnel. A new reference is held on the returned tunnel. */
197159
struct l2tp_tunnel *l2tp_tunnel_get(const struct net *net, u32 tunnel_id)
198160
{
@@ -345,13 +307,11 @@ int l2tp_session_register(struct l2tp_session *session,
345307
}
346308

347309
l2tp_tunnel_inc_refcount(tunnel);
348-
sock_hold(tunnel->sock);
349310
hlist_add_head_rcu(&session->global_hlist, g_head);
350311

351312
spin_unlock_bh(&pn->l2tp_session_hlist_lock);
352313
} else {
353314
l2tp_tunnel_inc_refcount(tunnel);
354-
sock_hold(tunnel->sock);
355315
}
356316

357317
hlist_add_head(&session->hlist, head);
@@ -975,21 +935,18 @@ int l2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
975935
{
976936
struct l2tp_tunnel *tunnel;
977937

978-
tunnel = l2tp_sock_to_tunnel(sk);
938+
tunnel = l2tp_tunnel(sk);
979939
if (tunnel == NULL)
980940
goto pass_up;
981941

982942
l2tp_dbg(tunnel, L2TP_MSG_DATA, "%s: received %d bytes\n",
983943
tunnel->name, skb->len);
984944

985945
if (l2tp_udp_recv_core(tunnel, skb, tunnel->recv_payload_hook))
986-
goto pass_up_put;
946+
goto pass_up;
987947

988-
sock_put(sk);
989948
return 0;
990949

991-
pass_up_put:
992-
sock_put(sk);
993950
pass_up:
994951
return 1;
995952
}
@@ -1223,7 +1180,6 @@ static void l2tp_tunnel_destruct(struct sock *sk)
12231180

12241181
l2tp_info(tunnel, L2TP_MSG_CONTROL, "%s: closing...\n", tunnel->name);
12251182

1226-
12271183
/* Disable udp encapsulation */
12281184
switch (tunnel->encap) {
12291185
case L2TP_ENCAPTYPE_UDP:
@@ -1246,12 +1202,11 @@ static void l2tp_tunnel_destruct(struct sock *sk)
12461202
list_del_rcu(&tunnel->list);
12471203
spin_unlock_bh(&pn->l2tp_tunnel_list_lock);
12481204

1249-
tunnel->sock = NULL;
1250-
l2tp_tunnel_dec_refcount(tunnel);
1251-
12521205
/* Call the original destructor */
12531206
if (sk->sk_destruct)
12541207
(*sk->sk_destruct)(sk);
1208+
1209+
kfree_rcu(tunnel, rcu);
12551210
end:
12561211
return;
12571212
}
@@ -1312,30 +1267,22 @@ EXPORT_SYMBOL_GPL(l2tp_tunnel_closeall);
13121267
/* Tunnel socket destroy hook for UDP encapsulation */
13131268
static void l2tp_udp_encap_destroy(struct sock *sk)
13141269
{
1315-
struct l2tp_tunnel *tunnel = l2tp_sock_to_tunnel(sk);
1316-
if (tunnel) {
1317-
l2tp_tunnel_closeall(tunnel);
1318-
sock_put(sk);
1319-
}
1270+
struct l2tp_tunnel *tunnel = l2tp_tunnel(sk);
1271+
1272+
if (tunnel)
1273+
l2tp_tunnel_delete(tunnel);
13201274
}
13211275

13221276
/* Workqueue tunnel deletion function */
13231277
static void l2tp_tunnel_del_work(struct work_struct *work)
13241278
{
1325-
struct l2tp_tunnel *tunnel = NULL;
1326-
struct socket *sock = NULL;
1327-
struct sock *sk = NULL;
1328-
1329-
tunnel = container_of(work, struct l2tp_tunnel, del_work);
1279+
struct l2tp_tunnel *tunnel = container_of(work, struct l2tp_tunnel,
1280+
del_work);
1281+
struct sock *sk = tunnel->sock;
1282+
struct socket *sock = sk->sk_socket;
13301283

13311284
l2tp_tunnel_closeall(tunnel);
13321285

1333-
sk = l2tp_tunnel_sock_lookup(tunnel);
1334-
if (!sk)
1335-
goto out;
1336-
1337-
sock = sk->sk_socket;
1338-
13391286
/* If the tunnel socket was created within the kernel, use
13401287
* the sk API to release it here.
13411288
*/
@@ -1346,8 +1293,10 @@ static void l2tp_tunnel_del_work(struct work_struct *work)
13461293
}
13471294
}
13481295

1349-
l2tp_tunnel_sock_put(sk);
1350-
out:
1296+
/* drop initial ref */
1297+
l2tp_tunnel_dec_refcount(tunnel);
1298+
1299+
/* drop workqueue ref */
13511300
l2tp_tunnel_dec_refcount(tunnel);
13521301
}
13531302

@@ -1600,13 +1549,22 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
16001549
sk->sk_user_data = tunnel;
16011550
}
16021551

1552+
/* Bump the reference count. The tunnel context is deleted
1553+
* only when this drops to zero. A reference is also held on
1554+
* the tunnel socket to ensure that it is not released while
1555+
* the tunnel is extant. Must be done before sk_destruct is
1556+
* set.
1557+
*/
1558+
refcount_set(&tunnel->ref_count, 1);
1559+
sock_hold(sk);
1560+
tunnel->sock = sk;
1561+
tunnel->fd = fd;
1562+
16031563
/* Hook on the tunnel socket destructor so that we can cleanup
16041564
* if the tunnel socket goes away.
16051565
*/
16061566
tunnel->old_sk_destruct = sk->sk_destruct;
16071567
sk->sk_destruct = &l2tp_tunnel_destruct;
1608-
tunnel->sock = sk;
1609-
tunnel->fd = fd;
16101568
lockdep_set_class_and_name(&sk->sk_lock.slock, &l2tp_socket_class, "l2tp_sock");
16111569

16121570
sk->sk_allocation = GFP_ATOMIC;
@@ -1616,11 +1574,6 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
16161574

16171575
/* Add tunnel to our list */
16181576
INIT_LIST_HEAD(&tunnel->list);
1619-
1620-
/* Bump the reference count. The tunnel context is deleted
1621-
* only when this drops to zero. Must be done before list insertion
1622-
*/
1623-
refcount_set(&tunnel->ref_count, 1);
16241577
spin_lock_bh(&pn->l2tp_tunnel_list_lock);
16251578
list_add_rcu(&tunnel->list, &pn->l2tp_tunnel_list);
16261579
spin_unlock_bh(&pn->l2tp_tunnel_list_lock);
@@ -1661,8 +1614,6 @@ void l2tp_session_free(struct l2tp_session *session)
16611614

16621615
if (tunnel) {
16631616
BUG_ON(tunnel->magic != L2TP_TUNNEL_MAGIC);
1664-
sock_put(tunnel->sock);
1665-
session->tunnel = NULL;
16661617
l2tp_tunnel_dec_refcount(tunnel);
16671618
}
16681619

net/l2tp/l2tp_core.h

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -219,27 +219,8 @@ static inline void *l2tp_session_priv(struct l2tp_session *session)
219219
return &session->priv[0];
220220
}
221221

222-
static inline struct l2tp_tunnel *l2tp_sock_to_tunnel(struct sock *sk)
223-
{
224-
struct l2tp_tunnel *tunnel;
225-
226-
if (sk == NULL)
227-
return NULL;
228-
229-
sock_hold(sk);
230-
tunnel = (struct l2tp_tunnel *)(sk->sk_user_data);
231-
if (tunnel == NULL) {
232-
sock_put(sk);
233-
goto out;
234-
}
235-
236-
BUG_ON(tunnel->magic != L2TP_TUNNEL_MAGIC);
237-
238-
out:
239-
return tunnel;
240-
}
241-
242222
struct l2tp_tunnel *l2tp_tunnel_get(const struct net *net, u32 tunnel_id);
223+
void l2tp_tunnel_free(struct l2tp_tunnel *tunnel);
243224

244225
struct l2tp_session *l2tp_session_get(const struct net *net,
245226
struct l2tp_tunnel *tunnel,
@@ -288,7 +269,7 @@ static inline void l2tp_tunnel_inc_refcount(struct l2tp_tunnel *tunnel)
288269
static inline void l2tp_tunnel_dec_refcount(struct l2tp_tunnel *tunnel)
289270
{
290271
if (refcount_dec_and_test(&tunnel->ref_count))
291-
kfree_rcu(tunnel, rcu);
272+
l2tp_tunnel_free(tunnel);
292273
}
293274

294275
/* Session reference counts. Incremented when code obtains a reference

net/l2tp/l2tp_ip.c

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -234,17 +234,13 @@ static void l2tp_ip_close(struct sock *sk, long timeout)
234234
static void l2tp_ip_destroy_sock(struct sock *sk)
235235
{
236236
struct sk_buff *skb;
237-
struct l2tp_tunnel *tunnel = l2tp_sock_to_tunnel(sk);
237+
struct l2tp_tunnel *tunnel = sk->sk_user_data;
238238

239239
while ((skb = __skb_dequeue_tail(&sk->sk_write_queue)) != NULL)
240240
kfree_skb(skb);
241241

242-
if (tunnel) {
243-
l2tp_tunnel_closeall(tunnel);
244-
sock_put(sk);
245-
}
246-
247-
sk_refcnt_debug_dec(sk);
242+
if (tunnel)
243+
l2tp_tunnel_delete(tunnel);
248244
}
249245

250246
static int l2tp_ip_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)

net/l2tp/l2tp_ip6.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -248,16 +248,14 @@ static void l2tp_ip6_close(struct sock *sk, long timeout)
248248

249249
static void l2tp_ip6_destroy_sock(struct sock *sk)
250250
{
251-
struct l2tp_tunnel *tunnel = l2tp_sock_to_tunnel(sk);
251+
struct l2tp_tunnel *tunnel = sk->sk_user_data;
252252

253253
lock_sock(sk);
254254
ip6_flush_pending_frames(sk);
255255
release_sock(sk);
256256

257-
if (tunnel) {
258-
l2tp_tunnel_closeall(tunnel);
259-
sock_put(sk);
260-
}
257+
if (tunnel)
258+
l2tp_tunnel_delete(tunnel);
261259

262260
inet6_destroy_sock(sk);
263261
}

0 commit comments

Comments
 (0)