qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Kevin Wolf <kwolf@redhat.com>
To: qemu-block@nongnu.org
Cc: kwolf@redhat.com, peter.maydell@linaro.org, qemu-devel@nongnu.org
Subject: [PULL 05/38] curl: Disconnect sockets from CURLState
Date: Thu, 11 Mar 2021 15:47:38 +0100	[thread overview]
Message-ID: <20210311144811.313451-6-kwolf@redhat.com> (raw)
In-Reply-To: <20210311144811.313451-1-kwolf@redhat.com>

From: Max Reitz <mreitz@redhat.com>

When a curl transfer is finished, that does not mean that CURL lets go
of all the sockets it used for it.  We therefore must not free a
CURLSocket object before CURL has invoked curl_sock_cb() to tell us to
remove it.  Otherwise, we may get a use-after-free, as described in this
bug report: https://bugs.launchpad.net/qemu/+bug/1916501

(Reproducer from that report:
  $ qemu-img convert -f qcow2 -O raw \
  https://download.cirros-cloud.net/0.4.0/cirros-0.4.0-x86_64-disk.img \
  out.img
)

(Alternatively, it might seem logical to force-drop all sockets that
have been used for a state when the respective transfer is done, kind of
like it is done now, but including unsetting the AIO handlers.
Unfortunately, doing so makes the driver just hang instead of crashing,
which seems to evidence that CURL still uses those sockets.)

Make the CURLSocket object independent of "its" CURLState by putting all
sockets into a hash table belonging to the BDRVCURLState instead of a
list that belongs to a CURLState.  Do not touch any sockets in
curl_clean_state().

Testing, it seems like all sockets are indeed gone by the time the curl
BDS is closed, so it seems like there really was no point in freeing any
socket just because a transfer is done.  libcurl does invoke
curl_sock_cb() with CURL_POLL_REMOVE for every socket it has.

Buglink: https://bugs.launchpad.net/qemu/+bug/1916501
Signed-off-by: Max Reitz <mreitz@redhat.com>
Message-Id: <20210309130541.37540-3-mreitz@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block/curl.c | 42 ++++++++++++++++++++++++------------------
 1 file changed, 24 insertions(+), 18 deletions(-)

diff --git a/block/curl.c b/block/curl.c
index 43c79bcf36..50e741a0d7 100644
--- a/block/curl.c
+++ b/block/curl.c
@@ -79,7 +79,6 @@ typedef struct CURLAIOCB {
 typedef struct CURLSocket {
     int fd;
     struct BDRVCURLState *s;
-    QLIST_ENTRY(CURLSocket) next;
 } CURLSocket;
 
 typedef struct CURLState
@@ -87,7 +86,6 @@ typedef struct CURLState
     struct BDRVCURLState *s;
     CURLAIOCB *acb[CURL_NUM_ACB];
     CURL *curl;
-    QLIST_HEAD(, CURLSocket) sockets;
     char *orig_buf;
     uint64_t buf_start;
     size_t buf_off;
@@ -102,6 +100,7 @@ typedef struct BDRVCURLState {
     QEMUTimer timer;
     uint64_t len;
     CURLState states[CURL_NUM_STATES];
+    GHashTable *sockets; /* GINT_TO_POINTER(fd) -> socket */
     char *url;
     size_t readahead_size;
     bool sslverify;
@@ -120,6 +119,21 @@ typedef struct BDRVCURLState {
 static void curl_clean_state(CURLState *s);
 static void curl_multi_do(void *arg);
 
+static gboolean curl_drop_socket(void *key, void *value, void *opaque)
+{
+    CURLSocket *socket = value;
+    BDRVCURLState *s = socket->s;
+
+    aio_set_fd_handler(s->aio_context, socket->fd, false,
+                       NULL, NULL, NULL, NULL);
+    return true;
+}
+
+static void curl_drop_all_sockets(GHashTable *sockets)
+{
+    g_hash_table_foreach_remove(sockets, curl_drop_socket, NULL);
+}
+
 /* Called from curl_multi_do_locked, with s->mutex held.  */
 static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque)
 {
@@ -147,16 +161,12 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
     curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char **)&state);
     s = state->s;
 
-    QLIST_FOREACH(socket, &state->sockets, next) {
-        if (socket->fd == fd) {
-            break;
-        }
-    }
+    socket = g_hash_table_lookup(s->sockets, GINT_TO_POINTER(fd));
     if (!socket) {
         socket = g_new0(CURLSocket, 1);
         socket->fd = fd;
         socket->s = s;
-        QLIST_INSERT_HEAD(&state->sockets, socket, next);
+        g_hash_table_insert(s->sockets, GINT_TO_POINTER(fd), socket);
     }
 
     trace_curl_sock_cb(action, (int)fd);
@@ -180,8 +190,7 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
     }
 
     if (action == CURL_POLL_REMOVE) {
-        QLIST_REMOVE(socket, next);
-        g_free(socket);
+        g_hash_table_remove(s->sockets, GINT_TO_POINTER(fd));
     }
 
     return 0;
@@ -498,7 +507,6 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state)
 #endif
     }
 
-    QLIST_INIT(&state->sockets);
     state->s = s;
 
     return 0;
@@ -515,13 +523,6 @@ static void curl_clean_state(CURLState *s)
     if (s->s->multi)
         curl_multi_remove_handle(s->s->multi, s->curl);
 
-    while (!QLIST_EMPTY(&s->sockets)) {
-        CURLSocket *socket = QLIST_FIRST(&s->sockets);
-
-        QLIST_REMOVE(socket, next);
-        g_free(socket);
-    }
-
     s->in_use = 0;
 
     qemu_co_enter_next(&s->s->free_state_waitq, &s->s->mutex);
@@ -539,6 +540,7 @@ static void curl_detach_aio_context(BlockDriverState *bs)
     int i;
 
     WITH_QEMU_LOCK_GUARD(&s->mutex) {
+        curl_drop_all_sockets(s->sockets);
         for (i = 0; i < CURL_NUM_STATES; i++) {
             if (s->states[i].in_use) {
                 curl_clean_state(&s->states[i]);
@@ -745,6 +747,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
     qemu_co_queue_init(&s->free_state_waitq);
     s->aio_context = bdrv_get_aio_context(bs);
     s->url = g_strdup(file);
+    s->sockets = g_hash_table_new_full(NULL, NULL, NULL, g_free);
     qemu_mutex_lock(&s->mutex);
     state = curl_find_state(s);
     qemu_mutex_unlock(&s->mutex);
@@ -818,6 +821,8 @@ out_noclean:
     g_free(s->username);
     g_free(s->proxyusername);
     g_free(s->proxypassword);
+    curl_drop_all_sockets(s->sockets);
+    g_hash_table_destroy(s->sockets);
     qemu_opts_del(opts);
     return -EINVAL;
 }
@@ -916,6 +921,7 @@ static void curl_close(BlockDriverState *bs)
     curl_detach_aio_context(bs);
     qemu_mutex_destroy(&s->mutex);
 
+    g_hash_table_destroy(s->sockets);
     g_free(s->cookie);
     g_free(s->url);
     g_free(s->username);
-- 
2.29.2



  parent reply	other threads:[~2021-03-11 15:33 UTC|newest]

Thread overview: 42+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-03-11 14:47 [PULL 00/38] Block layer patches and object-add QAPIfication Kevin Wolf
2021-03-11 14:47 ` [PULL 01/38] block: remove format defaults from QemuOpts in bdrv_create_file() Kevin Wolf
2021-03-11 14:47 ` [PULL 02/38] storage-daemon: Call job_cancel_sync_all() on shutdown Kevin Wolf
2021-03-11 14:47 ` [PULL 03/38] stream: Don't crash when node permission is denied Kevin Wolf
2021-03-11 14:47 ` [PULL 04/38] curl: Store BDRVCURLState pointer in CURLSocket Kevin Wolf
2021-03-11 14:47 ` Kevin Wolf [this message]
2021-03-11 14:47 ` [PULL 06/38] block/export: disable VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD for now Kevin Wolf
2021-03-11 14:47 ` [PULL 07/38] test: new qTest case to test the vhost-user-blk-server Kevin Wolf
2021-03-11 14:47 ` [PULL 08/38] tests/qtest: add multi-queue test case to vhost-user-blk-test Kevin Wolf
2021-03-11 14:47 ` [PULL 09/38] vhost-user-blk-test: test discard/write zeroes invalid inputs Kevin Wolf
2021-03-11 14:47 ` [PULL 10/38] tests: Drop 'props' from object-add calls Kevin Wolf
2021-03-11 14:47 ` [PULL 11/38] qapi/qom: Drop deprecated 'props' from object-add Kevin Wolf
2021-03-11 14:47 ` [PULL 12/38] qapi/qom: Add ObjectOptions for iothread Kevin Wolf
2021-03-11 14:47 ` [PULL 13/38] qapi/qom: Add ObjectOptions for authz-* Kevin Wolf
2021-03-11 14:47 ` [PULL 14/38] qapi/qom: Add ObjectOptions for cryptodev-* Kevin Wolf
2021-03-11 14:47 ` [PULL 15/38] qapi/qom: Add ObjectOptions for dbus-vmstate Kevin Wolf
2021-03-11 14:47 ` [PULL 16/38] qapi/qom: Add ObjectOptions for memory-backend-* Kevin Wolf
2021-03-11 14:47 ` [PULL 17/38] qapi/qom: Add ObjectOptions for rng-*, deprecate 'opened' Kevin Wolf
2021-03-11 14:47 ` [PULL 18/38] qapi/qom: Add ObjectOptions for throttle-group Kevin Wolf
2021-03-11 14:47 ` [PULL 19/38] qapi/qom: Add ObjectOptions for secret*, deprecate 'loaded' Kevin Wolf
2021-03-11 14:47 ` [PULL 20/38] qapi/qom: Add ObjectOptions for tls-*, " Kevin Wolf
2021-03-11 14:47 ` [PULL 21/38] qapi/qom: Add ObjectOptions for can-* Kevin Wolf
2021-03-11 14:47 ` [PULL 22/38] qapi/qom: Add ObjectOptions for colo-compare Kevin Wolf
2021-03-11 14:47 ` [PULL 23/38] qapi/qom: Add ObjectOptions for filter-* Kevin Wolf
2021-03-11 14:47 ` [PULL 24/38] qapi/qom: Add ObjectOptions for pr-manager-helper Kevin Wolf
2021-03-11 14:47 ` [PULL 25/38] qapi/qom: Add ObjectOptions for confidential-guest-support Kevin Wolf
2021-03-11 14:47 ` [PULL 26/38] qapi/qom: Add ObjectOptions for input-* Kevin Wolf
2021-03-11 14:48 ` [PULL 27/38] qapi/qom: Add ObjectOptions for x-remote-object Kevin Wolf
2021-03-11 14:48 ` [PULL 28/38] qapi/qom: QAPIfy object-add Kevin Wolf
2021-03-11 14:48 ` [PULL 29/38] qom: Make "object" QemuOptsList optional Kevin Wolf
2021-03-11 14:48 ` [PULL 30/38] qemu-storage-daemon: Implement --object with qmp_object_add() Kevin Wolf
2021-03-11 14:48 ` [PULL 31/38] qom: Remove user_creatable_add_dict() Kevin Wolf
2021-03-11 14:48 ` [PULL 32/38] qom: Factor out user_creatable_process_cmdline() Kevin Wolf
2021-03-11 14:48 ` [PULL 33/38] qemu-io: Use user_creatable_process_cmdline() for --object Kevin Wolf
2021-03-11 14:48 ` [PULL 34/38] qemu-nbd: " Kevin Wolf
2021-03-11 14:48 ` [PULL 35/38] qom: Add user_creatable_add_from_str() Kevin Wolf
2021-03-11 14:48 ` [PULL 36/38] qemu-img: Use user_creatable_process_cmdline() for --object Kevin Wolf
2021-03-11 14:48 ` [PULL 37/38] hmp: QAPIfy object_add Kevin Wolf
2021-03-11 14:48 ` [PULL 38/38] qom: Add user_creatable_parse_str() Kevin Wolf
2021-03-12 18:56 ` [PULL 00/38] Block layer patches and object-add QAPIfication Peter Maydell
2021-03-15 12:08   ` Kevin Wolf
2021-03-15 12:10     ` Peter Maydell

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210311144811.313451-6-kwolf@redhat.com \
    --to=kwolf@redhat.com \
    --cc=peter.maydell@linaro.org \
    --cc=qemu-block@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).