[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <KU1P153MB016639445D1816077E1A7D94BFDD0@KU1P153MB0166.APCP153.PROD.OUTLOOK.COM>
Date: Mon, 29 Jul 2019 20:23:02 +0000
From: Dexuan Cui <decui@...rosoft.com>
To: Sunil Muthuswamy <sunilmut@...rosoft.com>,
David Miller <davem@...emloft.net>,
"netdev@...r.kernel.org" <netdev@...r.kernel.org>
CC: KY Srinivasan <kys@...rosoft.com>,
Haiyang Zhang <haiyangz@...rosoft.com>,
Stephen Hemminger <sthemmin@...rosoft.com>,
"sashal@...nel.org" <sashal@...nel.org>,
Michael Kelley <mikelley@...rosoft.com>,
"linux-hyperv@...r.kernel.org" <linux-hyperv@...r.kernel.org>,
"linux-kernel@...r.kernel.org" <linux-kernel@...r.kernel.org>,
"olaf@...fle.de" <olaf@...fle.de>,
"apw@...onical.com" <apw@...onical.com>,
"jasowang@...hat.com" <jasowang@...hat.com>,
vkuznets <vkuznets@...hat.com>,
"marcelo.cerri@...onical.com" <marcelo.cerri@...onical.com>
Subject: RE: [PATCH net] hv_sock: Fix hang when a connection is closed
> From: Sunil Muthuswamy <sunilmut@...rosoft.com>
> Sent: Monday, July 29, 2019 10:21 AM
> > --- a/net/vmw_vsock/hyperv_transport.c
> > +++ b/net/vmw_vsock/hyperv_transport.c
> > @@ -309,9 +309,16 @@ static void hvs_close_connection(struct
> vmbus_channel *chan)
> > {
> > struct sock *sk = get_per_channel_state(chan);
> >
> > + /* Grab an extra reference since hvs_do_close_lock_held() may decrease
> > + * the reference count to 0 by calling sock_put(sk).
> > + */
> > + sock_hold(sk);
> > +
>
> To me, it seems like when 'hvs_close_connection' is called, there should always
> be an outstanding reference to the socket.
I agree. There *should* be, but it turns out there is race condition:
For an established connectin that is being closed by the guest, the refcnt is 4
at the end of hvs_release() (Note: here the 'remove_sock' is false):
1 for the initial value;
1 for the sk being in the bound list;
1 for the sk being in the connected list;
1 for the delayed close_work.
After hvs_release() finishes, __vsock_release() -> sock_put(sk) *may* decrease
the refcnt to 3.
Concurrently, hvs_close_connection() runs in another thread:
calls vsock_remove_sock() to decrease the refcnt by 2;
call sock_put() to decrease the refcnt to 0, and free the sk;
Next, the "release_sock(sk)" may hang due to use-after-free.
In the above, after hvs_release() finishes, if hvs_close_connection() runs
faster than "__vsock_release() -> sock_put(sk)", then there is not any issue,
because at the beginning of hvs_close_connection(), the refcnt is still 4.
So, this patch can work, but it's not the right fix.
Your suggestion is correct and here is the patch.
I'll give it more tests and send a v2.
--- a/net/vmw_vsock/hyperv_transport.c
+++ b/net/vmw_vsock/hyperv_transport.c
@@ -312,6 +312,11 @@ static void hvs_close_connection(struct vmbus_channel *chan)
lock_sock(sk);
hvs_do_close_lock_held(vsock_sk(sk), true);
release_sock(sk);
+
+ /* Release the refcnt for the channel that's opened in
+ * hvs_open_connection().
+ */
+ sock_put(sk);
}
static void hvs_open_connection(struct vmbus_channel *chan)
@@ -407,6 +412,9 @@ static void hvs_open_connection(struct vmbus_channel *chan)
}
set_per_channel_state(chan, conn_from_host ? new : sk);
+
+ /* This reference will be dropped by hvs_close_connection(). */
+ sock_hold(conn_from_host ? new: sk);
vmbus_set_chn_rescind_callback(chan, hvs_close_connection);
/* Set the pending send size to max packet size to always get
> The reference that is dropped by
> ' hvs_do_close_lock_held' is a legitimate reference that was taken by
> 'hvs_close_lock_held'.
Correct.
> Or, in other words, I think the right solution is to always maintain a reference to
> socket
> until this routine is called and drop that here. That can be done by taking the
> reference to
> the socket prior to ' vmbus_set_chn_rescind_callback(chan,
> hvs_close_connection)' and
> dropping that reference at the end of 'hvs_close_connection'.
>
> > lock_sock(sk);
> > hvs_do_close_lock_held(vsock_sk(sk), true);
> > release_sock(sk);
> > +
> > + sock_put(sk);
>
> Thanks for taking a look at this. We should queue this fix and the other
> hvsocket fixes
> for the stable branch.
I added a "Cc: stable@...r.kernel.org" tag so this pach will go to the
stable kernels automatically.
Your previous two fixes are in the v5.2.4 stable kernel, but not in the other
longterm stable kernels 4.19 and 4.14:
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/log/?h=v5.2.4&qt=author&q=Muthuswamy
I'll request them to be backported for 4.19 and 4.14.
I'll also request the patch "vsock: correct removal of socket from the list"
to be backported.
The other two "hv_sock: perf" patches are more of features rather than
fixes. Usually the stable kernel maintaners don't backport feature patches.
Thanks,
-- Dexuan
Powered by blists - more mailing lists