Skip to content

Commit 5b950ff

Browse files
Paolo Abenidavem330
authored andcommitted
mptcp: link MPC subflow into msk only after accept
Christoph reported the following splat: WARNING: CPU: 0 PID: 4615 at net/ipv4/inet_connection_sock.c:1031 inet_csk_listen_stop+0x8e8/0xad0 net/ipv4/inet_connection_sock.c:1031 Modules linked in: CPU: 0 PID: 4615 Comm: syz-executor.4 Not tainted 5.9.0 #37 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.13.0-0-gf21b5a4aeb02-prebuilt.qemu.org 04/01/2014 RIP: 0010:inet_csk_listen_stop+0x8e8/0xad0 net/ipv4/inet_connection_sock.c:1031 Code: 03 00 00 00 e8 79 b2 3d ff e9 ad f9 ff ff e8 1f 76 ba fe be 02 00 00 00 4c 89 f7 e8 62 b2 3d ff e9 14 f9 ff ff e8 08 76 ba fe <0f> 0b e9 97 f8 ff ff e8 fc 75 ba fe be 03 00 00 00 4c 89 f7 e8 3f RSP: 0018:ffffc900037f7948 EFLAGS: 00010293 RAX: ffff88810a349c80 RBX: ffff888114ee1b00 RCX: ffffffff827b14cd RDX: 0000000000000000 RSI: ffffffff827b1c38 RDI: 0000000000000005 RBP: ffff88810a2a8000 R08: ffff88810a349c80 R09: fffff520006fef1f R10: 0000000000000003 R11: fffff520006fef1e R12: ffff888114ee2d00 R13: dffffc0000000000 R14: 0000000000000001 R15: ffff888114ee1d68 FS: 00007f2ac1945700(0000) GS:ffff88811b400000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007ffd44798bc0 CR3: 0000000109810002 CR4: 0000000000170ef0 Call Trace: __tcp_close+0xd86/0x1110 net/ipv4/tcp.c:2433 __mptcp_close_ssk+0x256/0x430 net/mptcp/protocol.c:1761 __mptcp_destroy_sock+0x49b/0x770 net/mptcp/protocol.c:2127 mptcp_close+0x62d/0x910 net/mptcp/protocol.c:2184 inet_release+0xe9/0x1f0 net/ipv4/af_inet.c:434 __sock_release+0xd2/0x280 net/socket.c:596 sock_close+0x15/0x20 net/socket.c:1277 __fput+0x276/0x960 fs/file_table.c:281 task_work_run+0x109/0x1d0 kernel/task_work.c:151 get_signal+0xe8f/0x1d40 kernel/signal.c:2561 arch_do_signal+0x88/0x1b60 arch/x86/kernel/signal.c:811 exit_to_user_mode_loop kernel/entry/common.c:161 [inline] exit_to_user_mode_prepare+0x9b/0xf0 kernel/entry/common.c:191 syscall_exit_to_user_mode+0x22/0x150 kernel/entry/common.c:266 entry_SYSCALL_64_after_hwframe+0x44/0xa9 RIP: 0033:0x7f2ac1254469 Code: 00 f3 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d ff 49 2b 00 f7 d8 64 89 01 48 RSP: 002b:00007f2ac1944dc8 EFLAGS: 00000246 ORIG_RAX: 0000000000000010 RAX: ffffffffffffffbf RBX: 000000000069bf00 RCX: 00007f2ac1254469 RDX: 0000000000000000 RSI: 0000000000008982 RDI: 0000000000000003 RBP: 000000000069bf00 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 000000000069bf0c R13: 00007ffeb53f178f R14: 00000000004668b0 R15: 0000000000000003 After commit 0397c6d ("mptcp: keep unaccepted MPC subflow into join list"), the msk's workqueue and/or PM can touch the MPC subflow - and acquire its socket lock - even if it's still unaccepted. If the above event races with the relevant listener socket close, we can end-up with the above splat. This change addresses the issue delaying the MPC socket insertion in conn_list at accept time - that is, partially reverting the blamed commit. We must additionally ensure that mptcp_pm_fully_established() happens after accept() time, or the PM will not be able to handle properly such event - conn_list could be empty otherwise. In the receive path, we check the subflow list node to ensure it is out of the listener queue. Be sure client subflows do not match transiently such condition moving them into the join list earlier at creation time. Since we now have multiple mptcp_pm_fully_established() call sites from different code-paths, said helper can now race with itself. Use an additional PM status bit to avoid multiple notifications. Reported-by: Christoph Paasch <cpaasch@apple.com> Closes: multipath-tcp/mptcp_net-next#103 Fixes: 0397c6d ("mptcp: keep unaccepted MPC subflow into join list"), Reviewed-by: Matthieu Baerts <matthieu.baerts@tessares.net> Signed-off-by: Paolo Abeni <pabeni@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 7bdddc6 commit 5b950ff

File tree

5 files changed

+35
-6
lines changed

5 files changed

+35
-6
lines changed

net/mptcp/options.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -802,7 +802,12 @@ static bool check_fully_established(struct mptcp_sock *msk, struct sock *ssk,
802802
mptcp_subflow_fully_established(subflow, mp_opt);
803803

804804
fully_established:
805-
if (likely(subflow->pm_notified))
805+
/* if the subflow is not already linked into the conn_list, we can't
806+
* notify the PM: this subflow is still on the listener queue
807+
* and the PM possibly acquiring the subflow lock could race with
808+
* the listener close
809+
*/
810+
if (likely(subflow->pm_notified) || list_empty(&subflow->node))
806811
return true;
807812

808813
subflow->pm_notified = 1;

net/mptcp/pm.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,14 @@ void mptcp_pm_fully_established(struct mptcp_sock *msk)
126126

127127
spin_lock_bh(&pm->lock);
128128

129-
if (READ_ONCE(pm->work_pending))
129+
/* mptcp_pm_fully_established() can be invoked by multiple
130+
* racing paths - accept() and check_fully_established()
131+
* be sure to serve this event only once.
132+
*/
133+
if (READ_ONCE(pm->work_pending) &&
134+
!(msk->pm.status & BIT(MPTCP_PM_ALREADY_ESTABLISHED)))
130135
mptcp_pm_schedule_work(msk, MPTCP_PM_ESTABLISHED);
136+
msk->pm.status |= BIT(MPTCP_PM_ALREADY_ESTABLISHED);
131137

132138
spin_unlock_bh(&pm->lock);
133139
}

net/mptcp/protocol.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3208,6 +3208,17 @@ static int mptcp_stream_accept(struct socket *sock, struct socket *newsock,
32083208
bool slowpath;
32093209

32103210
slowpath = lock_sock_fast(newsk);
3211+
3212+
/* PM/worker can now acquire the first subflow socket
3213+
* lock without racing with listener queue cleanup,
3214+
* we can notify it, if needed.
3215+
*/
3216+
subflow = mptcp_subflow_ctx(msk->first);
3217+
list_add(&subflow->node, &msk->conn_list);
3218+
sock_hold(msk->first);
3219+
if (mptcp_is_fully_established(newsk))
3220+
mptcp_pm_fully_established(msk);
3221+
32113222
mptcp_copy_inaddrs(newsk, msk->first);
32123223
mptcp_rcv_space_init(msk, msk->first);
32133224

net/mptcp/protocol.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ enum mptcp_pm_status {
165165
MPTCP_PM_ADD_ADDR_SEND_ACK,
166166
MPTCP_PM_RM_ADDR_RECEIVED,
167167
MPTCP_PM_ESTABLISHED,
168+
MPTCP_PM_ALREADY_ESTABLISHED, /* persistent status, set after ESTABLISHED event */
168169
MPTCP_PM_SUBFLOW_ESTABLISHED,
169170
};
170171

net/mptcp/subflow.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -614,8 +614,9 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk,
614614
*/
615615
inet_sk_state_store((void *)new_msk, TCP_ESTABLISHED);
616616

617-
/* link the newly created socket to the msk */
618-
mptcp_add_pending_subflow(mptcp_sk(new_msk), ctx);
617+
/* record the newly created socket as the first msk
618+
* subflow, but don't link it yet into conn_list
619+
*/
619620
WRITE_ONCE(mptcp_sk(new_msk)->first, child);
620621

621622
/* new mpc subflow takes ownership of the newly
@@ -1148,13 +1149,18 @@ int __mptcp_subflow_connect(struct sock *sk, const struct mptcp_addr_info *loc,
11481149
subflow->request_bkup = !!(loc->flags & MPTCP_PM_ADDR_FLAG_BACKUP);
11491150
mptcp_info2sockaddr(remote, &addr);
11501151

1152+
mptcp_add_pending_subflow(msk, subflow);
11511153
err = kernel_connect(sf, (struct sockaddr *)&addr, addrlen, O_NONBLOCK);
11521154
if (err && err != -EINPROGRESS)
1153-
goto failed;
1155+
goto failed_unlink;
11541156

1155-
mptcp_add_pending_subflow(msk, subflow);
11561157
return err;
11571158

1159+
failed_unlink:
1160+
spin_lock_bh(&msk->join_list_lock);
1161+
list_del(&subflow->node);
1162+
spin_unlock_bh(&msk->join_list_lock);
1163+
11581164
failed:
11591165
subflow->disposable = 1;
11601166
sock_release(sf);

0 commit comments

Comments
 (0)