Re: [PATCH v2 01/10] block/nbd: introduce NBDConnectThread reference counter

2021-04-08 Thread Roman Kagan
On Thu, Apr 08, 2021 at 05:08:18PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> The structure is shared between NBD BDS and connection thread. And it
> is possible the connect thread will finish after closing and releasing
> for the bs. To handle this we have a concept of
> CONNECT_THREAD_RUNNING_DETACHED state and when thread is running and
> BDS is going to be closed we don't free the structure, but instead move
> it to CONNECT_THREAD_RUNNING_DETACHED state, so that thread will free
> it.
> 
> Still more native way to solve the problem is using reference counter
> for shared structure. Let's use it. It makes code smaller and more
> readable.
> 
> New approach also makes checks in nbd_co_establish_connection()
> redundant: now we are sure that s->connect_thread is valid during the
> whole life of NBD BDS.
> 
> This also fixes possible use-after-free of s->connect_thread if
> nbd_co_establish_connection_cancel() clears it during
> nbd_co_establish_connection(), and nbd_co_establish_connection() uses
> local copy of s->connect_thread after yield point.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy 
> ---
>  block/nbd.c | 62 +
>  1 file changed, 20 insertions(+), 42 deletions(-)

Reviewed-by: Roman Kagan 



[PATCH v2 01/10] block/nbd: introduce NBDConnectThread reference counter

2021-04-08 Thread Vladimir Sementsov-Ogievskiy
The structure is shared between NBD BDS and connection thread. And it
is possible the connect thread will finish after closing and releasing
for the bs. To handle this we have a concept of
CONNECT_THREAD_RUNNING_DETACHED state and when thread is running and
BDS is going to be closed we don't free the structure, but instead move
it to CONNECT_THREAD_RUNNING_DETACHED state, so that thread will free
it.

Still more native way to solve the problem is using reference counter
for shared structure. Let's use it. It makes code smaller and more
readable.

New approach also makes checks in nbd_co_establish_connection()
redundant: now we are sure that s->connect_thread is valid during the
whole life of NBD BDS.

This also fixes possible use-after-free of s->connect_thread if
nbd_co_establish_connection_cancel() clears it during
nbd_co_establish_connection(), and nbd_co_establish_connection() uses
local copy of s->connect_thread after yield point.

Signed-off-by: Vladimir Sementsov-Ogievskiy 
---
 block/nbd.c | 62 +
 1 file changed, 20 insertions(+), 42 deletions(-)

diff --git a/block/nbd.c b/block/nbd.c
index c26dc5a54f..64b2977dc8 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -73,12 +73,6 @@ typedef enum NBDConnectThreadState {
 /* Thread is running, no results for now */
 CONNECT_THREAD_RUNNING,
 
-/*
- * Thread is running, but requestor exited. Thread should close
- * the new socket and free the connect state on exit.
- */
-CONNECT_THREAD_RUNNING_DETACHED,
-
 /* Thread finished, results are stored in a state */
 CONNECT_THREAD_FAIL,
 CONNECT_THREAD_SUCCESS
@@ -101,6 +95,8 @@ typedef struct NBDConnectThread {
 QIOChannelSocket *sioc;
 Error *err;
 
+int refcnt; /* atomic access */
+
 /* state and bh_ctx are protected by mutex */
 QemuMutex mutex;
 NBDConnectThreadState state; /* current state of the thread */
@@ -144,16 +140,18 @@ typedef struct BDRVNBDState {
 NBDConnectThread *connect_thread;
 } BDRVNBDState;
 
+static void connect_thread_state_unref(NBDConnectThread *thr);
 static int nbd_establish_connection(BlockDriverState *bs, SocketAddress *saddr,
 Error **errp);
 static int nbd_co_establish_connection(BlockDriverState *bs, Error **errp);
-static void nbd_co_establish_connection_cancel(BlockDriverState *bs,
-   bool detach);
+static void nbd_co_establish_connection_cancel(BlockDriverState *bs);
 static int nbd_client_handshake(BlockDriverState *bs, Error **errp);
 static void nbd_yank(void *opaque);
 
 static void nbd_clear_bdrvstate(BDRVNBDState *s)
 {
+connect_thread_state_unref(s->connect_thread);
+s->connect_thread = NULL;
 object_unref(OBJECT(s->tlscreds));
 qapi_free_SocketAddress(s->saddr);
 s->saddr = NULL;
@@ -293,7 +291,7 @@ static void coroutine_fn 
nbd_client_co_drain_begin(BlockDriverState *bs)
 qemu_co_sleep_wake(s->connection_co_sleep_ns_state);
 }
 
-nbd_co_establish_connection_cancel(bs, false);
+nbd_co_establish_connection_cancel(bs);
 
 reconnect_delay_timer_del(s);
 
@@ -333,7 +331,7 @@ static void nbd_teardown_connection(BlockDriverState *bs)
 if (s->connection_co_sleep_ns_state) {
 qemu_co_sleep_wake(s->connection_co_sleep_ns_state);
 }
-nbd_co_establish_connection_cancel(bs, true);
+nbd_co_establish_connection_cancel(bs);
 }
 if (qemu_in_coroutine()) {
 s->teardown_co = qemu_coroutine_self();
@@ -376,26 +374,28 @@ static void nbd_init_connect_thread(BDRVNBDState *s)
 .state = CONNECT_THREAD_NONE,
 .bh_func = connect_bh,
 .bh_opaque = s,
+.refcnt = 1,
 };
 
 qemu_mutex_init(>connect_thread->mutex);
 }
 
-static void nbd_free_connect_thread(NBDConnectThread *thr)
+static void connect_thread_state_unref(NBDConnectThread *thr)
 {
-if (thr->sioc) {
-qio_channel_close(QIO_CHANNEL(thr->sioc), NULL);
+if (qatomic_dec_fetch(>refcnt) == 0) {
+if (thr->sioc) {
+qio_channel_close(QIO_CHANNEL(thr->sioc), NULL);
+}
+error_free(thr->err);
+qapi_free_SocketAddress(thr->saddr);
+g_free(thr);
 }
-error_free(thr->err);
-qapi_free_SocketAddress(thr->saddr);
-g_free(thr);
 }
 
 static void *connect_thread_func(void *opaque)
 {
 NBDConnectThread *thr = opaque;
 int ret;
-bool do_free = false;
 
 thr->sioc = qio_channel_socket_new();
 
@@ -419,18 +419,13 @@ static void *connect_thread_func(void *opaque)
 thr->bh_ctx = NULL;
 }
 break;
-case CONNECT_THREAD_RUNNING_DETACHED:
-do_free = true;
-break;
 default:
 abort();
 }
 
 qemu_mutex_unlock(>mutex);
 
-if (do_free) {
-nbd_free_connect_thread(thr);
-}
+connect_thread_state_unref(thr);
 
 return NULL;
 }
@@ -451,6 +446,7 @@