lkml.org 
[lkml]   [2019]   [Aug]   [8]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.2 46/56] hv_sock: Fix hang when a connection is closed
    Date
    From: Dexuan Cui <decui@microsoft.com>

    [ Upstream commit 8c7885e5690be9a27231ebebf82ef29fbf46c4e4 ]

    There is a race condition for an established connection 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.

    The issue can be resolved if an extra reference is taken when the
    connection is established.

    Fixes: a9eeb998c28d ("hv_sock: Add support for delayed close")
    Signed-off-by: Dexuan Cui <decui@microsoft.com>
    Reviewed-by: Sunil Muthuswamy <sunilmut@microsoft.com>
    Signed-off-by: David S. Miller <davem@davemloft.net>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    net/vmw_vsock/hyperv_transport.c | 8 ++++++++
    1 file changed, 8 insertions(+)

    --- a/net/vmw_vsock/hyperv_transport.c
    +++ b/net/vmw_vsock/hyperv_transport.c
    @@ -311,6 +311,11 @@ static void hvs_close_connection(struct
    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)
    @@ -378,6 +383,9 @@ static void hvs_open_connection(struct v
    }

    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

    \
     
     \ /
      Last update: 2019-08-08 21:26    [W:2.982 / U:0.020 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site