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 after having
refactored the passive socket initialization part:

  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.

Reported-and-tested-by: Christoph Paasch <cpaasch@apple.com>
Closes: #347
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Reviewed-by: Matthieu Baerts <matthieu.baerts@tessares.net>
  • Loading branch information
Paolo Abeni authored and matttbe committed Feb 20, 2023
1 parent 8181540 commit 1d0f6c7
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 @@ -2398,9 +2398,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 @@ -2414,7 +2415,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 @@ -2623,6 +2632,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 @@ -2638,9 +2650,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 @@ -3078,6 +3087,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 @@ -3095,8 +3105,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 @@ -3152,8 +3161,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 @@ -3704,6 +3711,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 @@ -693,9 +693,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 @@ -1848,7 +1849,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 @@ -1935,6 +1935,13 @@ static void subflow_ulp_release(struct sock *ssk)
* the ctx alive, will be freed by __mptcp_close_ssk()
*/
release = ctx->disposable;

/* 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 1d0f6c7

Please sign in to comment.