Skip to content

Commit fd89767

Browse files
Paolo Abenikuba-moo
authored andcommitted
mptcp: be careful on MPTCP-level ack.
We can enter the main mptcp_recvmsg() loop even when no subflows are connected. As note by Eric, that would result in a divide by zero oops on ack generation. Address the issue by checking the subflow status before sending the ack. Additionally protect mptcp_recvmsg() against invocation with weird socket states. v1 -> v2: - removed unneeded inline keyword - Jakub Reported-and-suggested-by: Eric Dumazet <[email protected]> Fixes: ea4ca58 ("mptcp: refine MPTCP-level ack scheduling") Signed-off-by: Paolo Abeni <[email protected]> Link: https://lore.kernel.org/r/5370c0ae03449239e3d1674ddcfb090cf6f20abe.1606253206.git.pabeni@redhat.com Signed-off-by: Jakub Kicinski <[email protected]>
1 parent bfd0423 commit fd89767

File tree

1 file changed

+49
-18
lines changed

1 file changed

+49
-18
lines changed

net/mptcp/protocol.c

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -419,31 +419,57 @@ static bool mptcp_subflow_active(struct mptcp_subflow_context *subflow)
419419
return ((1 << ssk->sk_state) & (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT));
420420
}
421421

422-
static void mptcp_send_ack(struct mptcp_sock *msk, bool force)
422+
static bool tcp_can_send_ack(const struct sock *ssk)
423+
{
424+
return !((1 << inet_sk_state_load(ssk)) &
425+
(TCPF_SYN_SENT | TCPF_SYN_RECV | TCPF_TIME_WAIT | TCPF_CLOSE));
426+
}
427+
428+
static void mptcp_send_ack(struct mptcp_sock *msk)
423429
{
424430
struct mptcp_subflow_context *subflow;
425-
struct sock *pick = NULL;
426431

427432
mptcp_for_each_subflow(msk, subflow) {
428433
struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
429434

430-
if (force) {
431-
lock_sock(ssk);
435+
lock_sock(ssk);
436+
if (tcp_can_send_ack(ssk))
432437
tcp_send_ack(ssk);
433-
release_sock(ssk);
434-
continue;
435-
}
436-
437-
/* if the hintes ssk is still active, use it */
438-
pick = ssk;
439-
if (ssk == msk->ack_hint)
440-
break;
438+
release_sock(ssk);
441439
}
442-
if (!force && pick) {
443-
lock_sock(pick);
444-
tcp_cleanup_rbuf(pick, 1);
445-
release_sock(pick);
440+
}
441+
442+
static bool mptcp_subflow_cleanup_rbuf(struct sock *ssk)
443+
{
444+
int ret;
445+
446+
lock_sock(ssk);
447+
ret = tcp_can_send_ack(ssk);
448+
if (ret)
449+
tcp_cleanup_rbuf(ssk, 1);
450+
release_sock(ssk);
451+
return ret;
452+
}
453+
454+
static void mptcp_cleanup_rbuf(struct mptcp_sock *msk)
455+
{
456+
struct mptcp_subflow_context *subflow;
457+
458+
/* if the hinted ssk is still active, try to use it */
459+
if (likely(msk->ack_hint)) {
460+
mptcp_for_each_subflow(msk, subflow) {
461+
struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
462+
463+
if (msk->ack_hint == ssk &&
464+
mptcp_subflow_cleanup_rbuf(ssk))
465+
return;
466+
}
446467
}
468+
469+
/* otherwise pick the first active subflow */
470+
mptcp_for_each_subflow(msk, subflow)
471+
if (mptcp_subflow_cleanup_rbuf(mptcp_subflow_tcp_sock(subflow)))
472+
return;
447473
}
448474

449475
static bool mptcp_check_data_fin(struct sock *sk)
@@ -494,7 +520,7 @@ static bool mptcp_check_data_fin(struct sock *sk)
494520

495521
ret = true;
496522
mptcp_set_timeout(sk, NULL);
497-
mptcp_send_ack(msk, true);
523+
mptcp_send_ack(msk);
498524
mptcp_close_wake_up(sk);
499525
}
500526
return ret;
@@ -1579,6 +1605,11 @@ static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
15791605
return -EOPNOTSUPP;
15801606

15811607
lock_sock(sk);
1608+
if (unlikely(sk->sk_state == TCP_LISTEN)) {
1609+
copied = -ENOTCONN;
1610+
goto out_err;
1611+
}
1612+
15821613
timeo = sock_rcvtimeo(sk, nonblock);
15831614

15841615
len = min_t(size_t, len, INT_MAX);
@@ -1604,7 +1635,7 @@ static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
16041635
/* be sure to advertise window change */
16051636
old_space = READ_ONCE(msk->old_wspace);
16061637
if ((tcp_space(sk) - old_space) >= old_space)
1607-
mptcp_send_ack(msk, false);
1638+
mptcp_cleanup_rbuf(msk);
16081639

16091640
/* only the master socket status is relevant here. The exit
16101641
* conditions mirror closely tcp_recvmsg()

0 commit comments

Comments
 (0)