Skip to content

Commit

Permalink
mptcp: use the workqueue to destroy unaccepted sockets
Browse files Browse the repository at this point in the history
Christoph reported a UaF at token lookup time:

    BUG: KASAN: use-after-free in __token_bucket_busy+0x253/0x260
    Read of size 4 at addr ffff88810698d5b0 by task syz-executor653/3198

    CPU: 1 PID: 3198 Comm: syz-executor653 Not tainted 6.2.0-rc59af4eaa31c1f6c00c8f1e448ed99a45c66340dd5 #6
    Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.13.0-0-gf21b5a4aeb02-prebuilt.qemu.org 04/01/2014
    Call Trace:
     <TASK>
     dump_stack_lvl+0x6e/0x91
     print_report+0x16a/0x46f
     kasan_report+0xad/0x130
     __token_bucket_busy+0x253/0x260
     mptcp_token_new_connect+0x13d/0x490
     mptcp_connect+0x4ed/0x860
     __inet_stream_connect+0x80e/0xd90
     tcp_sendmsg_fastopen+0x3ce/0x710
     mptcp_sendmsg+0xff1/0x1a20
     inet_sendmsg+0x11d/0x140
     __sys_sendto+0x405/0x490
     __x64_sys_sendto+0xdc/0x1b0
     do_syscall_64+0x3b/0x90
     entry_SYSCALL_64_after_hwframe+0x72/0xdc

We need to properly clean-up all the paired MPTCP-level
resources and be sure to release the msk last, even when
the unaccepted subflow is destroyed by the TCP internals
via inet_child_forget().

We can re-use the existing MPTCP_WORK_CLOSE_SUBFLOW infra,
explicitly checking that for the critical scenario: the
closed subflow is the MPC one, the msk is not accepted and
eventually going through full cleanup.

With such change, __mptcp_destroy_sock() is always called
on msk sockets, even on accepted ones. We don't need anymore
to transiently drop one sk reference at msk clone time.

Fixes: 58b0991 ("mptcp: create msk early")
Reported-and-tested-by: Christoph Paasch <cpaasch@apple.com>
Closes: #347
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
  • Loading branch information
Paolo Abeni authored and matttbe committed Feb 17, 2023
1 parent 7e561d0 commit 38a89d2
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 12 deletions.
26 changes: 17 additions & 9 deletions net/mptcp/protocol.c
Original file line number Diff line number Diff line change
Expand Up @@ -2400,9 +2400,10 @@ static unsigned int mptcp_sync_mss(struct sock *sk, u32 pmtu)
return 0;
}

static void __mptcp_close_subflow(struct mptcp_sock *msk)
static void __mptcp_close_subflow(struct sock *sk)
{
struct mptcp_subflow_context *subflow, *tmp;
struct mptcp_sock *msk = mptcp_sk(sk);

might_sleep();

Expand All @@ -2416,7 +2417,15 @@ static void __mptcp_close_subflow(struct mptcp_sock *msk)
if (!skb_queue_empty_lockless(&ssk->sk_receive_queue))
continue;

mptcp_close_ssk((struct sock *)msk, ssk, subflow);
mptcp_close_ssk(sk, ssk, subflow);
}

/* if the MPC subflow has been closed before the msk is accepted,
* msk will never be accept-ed, close it now
*/
if (!msk->first && msk->in_accept_queue) {
sock_set_flag(sk, SOCK_DEAD);
inet_sk_state_store(sk, TCP_CLOSE);
}
}

Expand Down Expand Up @@ -2625,6 +2634,9 @@ static void mptcp_worker(struct work_struct *work)
__mptcp_check_send_data_fin(sk);
mptcp_check_data_fin(sk);

if (test_and_clear_bit(MPTCP_WORK_CLOSE_SUBFLOW, &msk->flags))
__mptcp_close_subflow(sk);

/* There is no point in keeping around an orphaned sk timedout or
* closed, but we need the msk around to reply to incoming DATA_FIN,
* even if it is orphaned and in FIN_WAIT2 state
Expand All @@ -2640,9 +2652,6 @@ static void mptcp_worker(struct work_struct *work)
}
}

if (test_and_clear_bit(MPTCP_WORK_CLOSE_SUBFLOW, &msk->flags))
__mptcp_close_subflow(msk);

if (test_and_clear_bit(MPTCP_WORK_RTX, &msk->flags))
__mptcp_retrans(sk);

Expand Down Expand Up @@ -3073,6 +3082,7 @@ struct sock *mptcp_sk_clone(const struct sock *sk,
msk->local_key = subflow_req->local_key;
msk->token = subflow_req->token;
msk->subflow = NULL;
msk->in_accept_queue = 1;
WRITE_ONCE(msk->fully_established, false);
if (mp_opt->suboptions & OPTION_MPTCP_CSUMREQD)
WRITE_ONCE(msk->csum_enabled, true);
Expand All @@ -3090,8 +3100,7 @@ struct sock *mptcp_sk_clone(const struct sock *sk,
security_inet_csk_clone(nsk, req);
bh_unlock_sock(nsk);

/* keep a single reference */
__sock_put(nsk);
/* note: the newly allocated socket refcount is 2 now */
return nsk;
}

Expand Down Expand Up @@ -3147,8 +3156,6 @@ static struct sock *mptcp_accept(struct sock *sk, int flags, int *err,
goto out;
}

/* acquire the 2nd reference for the owning socket */
sock_hold(new_mptcp_sock);
newsk = new_mptcp_sock;
MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_MPCAPABLEPASSIVEACK);
} else {
Expand Down Expand Up @@ -3696,6 +3703,7 @@ static int mptcp_stream_accept(struct socket *sock, struct socket *newsock,
struct sock *newsk = newsock->sk;

set_bit(SOCK_CUSTOM_SOCKOPT, &newsock->flags);
msk->in_accept_queue = 0;

lock_sock(newsk);

Expand Down
3 changes: 2 additions & 1 deletion net/mptcp/protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,8 @@ struct mptcp_sock {
u8 recvmsg_inq:1,
cork:1,
nodelay:1,
fastopening:1;
fastopening:1,
in_accept_queue:1;
int connect_flags;
struct work_struct work;
struct sk_buff *ooo_last_skb;
Expand Down
11 changes: 9 additions & 2 deletions net/mptcp/subflow.c
Original file line number Diff line number Diff line change
Expand Up @@ -692,9 +692,10 @@ static bool subflow_hmac_valid(const struct request_sock *req,

static void mptcp_force_close(struct sock *sk)
{
/* the msk is not yet exposed to user-space */
/* the msk is not yet exposed to user-space, and refcount is 2 */
inet_sk_state_store(sk, TCP_CLOSE);
sk_common_release(sk);
sock_put(sk);
}

static void subflow_ulp_fallback(struct sock *sk,
Expand Down Expand Up @@ -1841,7 +1842,6 @@ void mptcp_subflow_queue_clean(struct sock *listener_sk, struct sock *listener_s
struct sock *sk = (struct sock *)msk;
bool do_cancel_work;

sock_hold(sk);
lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
next = msk->dl_next;
msk->first = NULL;
Expand Down Expand Up @@ -1929,6 +1929,13 @@ static void subflow_ulp_release(struct sock *ssk)
* when the subflow is still unaccepted
*/
release = ctx->disposable || list_empty(&ctx->node);

/* inet_child_forget() does not call sk_state_change(),
* explicitly trigger the socket close machinery
*/
if (!release && !test_and_set_bit(MPTCP_WORK_CLOSE_SUBFLOW,
&mptcp_sk(sk)->flags))
mptcp_schedule_work(sk);
sock_put(sk);
}

Expand Down

0 comments on commit 38a89d2

Please sign in to comment.