forked from rpms/qemu-kvm
i8c-stream-rhel
changed/i8c-stream-rhel/qemu-kvm-6.2.0-53.module+el8.10.0+22268+f82ccd96
parent
5bb5d3f6e3
commit
e44416c856
@ -0,0 +1,277 @@
|
|||||||
|
From a0b12780f3cb97abad0a2c54d185c298d3f589e7 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Eric Blake <eblake@redhat.com>
|
||||||
|
Date: Fri, 17 May 2024 21:50:15 -0500
|
||||||
|
Subject: [PATCH 2/3] iotests: test NBD+TLS+iothread
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
RH-Author: Eric Blake <eblake@redhat.com>
|
||||||
|
RH-MergeRequest: 398: nbd/server: CVE-2024-7409: Avoid use-after-free when closing server
|
||||||
|
RH-Jira: RHEL-52611
|
||||||
|
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
RH-Commit: [2/3] f522ff5156086a83a7327c379dd3ccd8b583a421 (ebblake/qemu-kvm)
|
||||||
|
|
||||||
|
Prevent regressions when using NBD with TLS in the presence of
|
||||||
|
iothreads, adding coverage the fix to qio channels made in the
|
||||||
|
previous patch.
|
||||||
|
|
||||||
|
The shell function pick_unused_port() was copied from
|
||||||
|
nbdkit.git/tests/functions.sh.in, where it had all authors from Red
|
||||||
|
Hat, agreeing to the resulting relicensing from 2-clause BSD to GPLv2.
|
||||||
|
|
||||||
|
CC: qemu-stable@nongnu.org
|
||||||
|
CC: "Richard W.M. Jones" <rjones@redhat.com>
|
||||||
|
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||||
|
Message-ID: <20240531180639.1392905-6-eblake@redhat.com>
|
||||||
|
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
|
||||||
|
|
||||||
|
(cherry picked from commit a73c99378022ebb785481e84cfe1e81097546268)
|
||||||
|
Jira: https://issues.redhat.com/browse/RHEL-52611
|
||||||
|
Conflicts:
|
||||||
|
tests/qemu-iotests/tests/nbd-tls-iothread{,.out} - drop unknown
|
||||||
|
"tls-hostname" parameter
|
||||||
|
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||||
|
---
|
||||||
|
tests/qemu-iotests/tests/nbd-tls-iothread | 167 ++++++++++++++++++
|
||||||
|
tests/qemu-iotests/tests/nbd-tls-iothread.out | 53 ++++++
|
||||||
|
2 files changed, 220 insertions(+)
|
||||||
|
create mode 100755 tests/qemu-iotests/tests/nbd-tls-iothread
|
||||||
|
create mode 100644 tests/qemu-iotests/tests/nbd-tls-iothread.out
|
||||||
|
|
||||||
|
diff --git a/tests/qemu-iotests/tests/nbd-tls-iothread b/tests/qemu-iotests/tests/nbd-tls-iothread
|
||||||
|
new file mode 100755
|
||||||
|
index 0000000000..9e747e2639
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/tests/qemu-iotests/tests/nbd-tls-iothread
|
||||||
|
@@ -0,0 +1,167 @@
|
||||||
|
+#!/usr/bin/env bash
|
||||||
|
+# group: rw quick
|
||||||
|
+#
|
||||||
|
+# Test of NBD+TLS+iothread
|
||||||
|
+#
|
||||||
|
+# Copyright (C) 2024 Red Hat, Inc.
|
||||||
|
+#
|
||||||
|
+# This program is free software; you can redistribute it and/or modify
|
||||||
|
+# it under the terms of the GNU General Public License as published by
|
||||||
|
+# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
+# (at your option) any later version.
|
||||||
|
+#
|
||||||
|
+# This program is distributed in the hope that it will be useful,
|
||||||
|
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
+# GNU General Public License for more details.
|
||||||
|
+#
|
||||||
|
+# You should have received a copy of the GNU General Public License
|
||||||
|
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
+#
|
||||||
|
+
|
||||||
|
+# creator
|
||||||
|
+owner=eblake@redhat.com
|
||||||
|
+
|
||||||
|
+seq=`basename $0`
|
||||||
|
+echo "QA output created by $seq"
|
||||||
|
+
|
||||||
|
+status=1 # failure is the default!
|
||||||
|
+
|
||||||
|
+_cleanup()
|
||||||
|
+{
|
||||||
|
+ _cleanup_qemu
|
||||||
|
+ _cleanup_test_img
|
||||||
|
+ rm -f "$dst_image"
|
||||||
|
+ tls_x509_cleanup
|
||||||
|
+}
|
||||||
|
+trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||||
|
+
|
||||||
|
+# get standard environment, filters and checks
|
||||||
|
+cd ..
|
||||||
|
+. ./common.rc
|
||||||
|
+. ./common.filter
|
||||||
|
+. ./common.qemu
|
||||||
|
+. ./common.tls
|
||||||
|
+. ./common.nbd
|
||||||
|
+
|
||||||
|
+_supported_fmt qcow2 # Hardcoded to qcow2 command line and QMP below
|
||||||
|
+_supported_proto file
|
||||||
|
+
|
||||||
|
+# pick_unused_port
|
||||||
|
+#
|
||||||
|
+# Picks and returns an "unused" port, setting the global variable
|
||||||
|
+# $port.
|
||||||
|
+#
|
||||||
|
+# This is inherently racy, but we need it because qemu does not currently
|
||||||
|
+# permit NBD+TLS over a Unix domain socket
|
||||||
|
+pick_unused_port ()
|
||||||
|
+{
|
||||||
|
+ if ! (ss --version) >/dev/null 2>&1; then
|
||||||
|
+ _notrun "ss utility required, skipped this test"
|
||||||
|
+ fi
|
||||||
|
+
|
||||||
|
+ # Start at a random port to make it less likely that two parallel
|
||||||
|
+ # tests will conflict.
|
||||||
|
+ port=$(( 50000 + (RANDOM%15000) ))
|
||||||
|
+ while ss -ltn | grep -sqE ":$port\b"; do
|
||||||
|
+ ((port++))
|
||||||
|
+ if [ $port -eq 65000 ]; then port=50000; fi
|
||||||
|
+ done
|
||||||
|
+ echo picked unused port
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+tls_x509_init
|
||||||
|
+
|
||||||
|
+size=1G
|
||||||
|
+DST_IMG="$TEST_DIR/dst.qcow2"
|
||||||
|
+
|
||||||
|
+echo
|
||||||
|
+echo "== preparing TLS creds and spare port =="
|
||||||
|
+
|
||||||
|
+pick_unused_port
|
||||||
|
+tls_x509_create_root_ca "ca1"
|
||||||
|
+tls_x509_create_server "ca1" "server1"
|
||||||
|
+tls_x509_create_client "ca1" "client1"
|
||||||
|
+tls_obj_base=tls-creds-x509,id=tls0,verify-peer=true,dir="${tls_dir}"
|
||||||
|
+
|
||||||
|
+echo
|
||||||
|
+echo "== preparing image =="
|
||||||
|
+
|
||||||
|
+_make_test_img $size
|
||||||
|
+$QEMU_IMG create -f qcow2 "$DST_IMG" $size | _filter_img_create
|
||||||
|
+
|
||||||
|
+echo
|
||||||
|
+echo === Starting Src QEMU ===
|
||||||
|
+echo
|
||||||
|
+
|
||||||
|
+_launch_qemu -machine q35 \
|
||||||
|
+ -object iothread,id=iothread0 \
|
||||||
|
+ -object "${tls_obj_base}"/client1,endpoint=client \
|
||||||
|
+ -device '{"driver":"pcie-root-port", "id":"root0", "multifunction":true,
|
||||||
|
+ "bus":"pcie.0"}' \
|
||||||
|
+ -device '{"driver":"virtio-scsi-pci", "id":"virtio_scsi_pci0",
|
||||||
|
+ "bus":"root0", "iothread":"iothread0"}' \
|
||||||
|
+ -device '{"driver":"scsi-hd", "id":"image1", "drive":"drive_image1",
|
||||||
|
+ "bus":"virtio_scsi_pci0.0"}' \
|
||||||
|
+ -blockdev '{"driver":"file", "cache":{"direct":true, "no-flush":false},
|
||||||
|
+ "filename":"'"$TEST_IMG"'", "node-name":"drive_sys1"}' \
|
||||||
|
+ -blockdev '{"driver":"qcow2", "node-name":"drive_image1",
|
||||||
|
+ "file":"drive_sys1"}'
|
||||||
|
+h1=$QEMU_HANDLE
|
||||||
|
+_send_qemu_cmd $h1 '{"execute": "qmp_capabilities"}' 'return'
|
||||||
|
+
|
||||||
|
+echo
|
||||||
|
+echo === Starting Dst VM2 ===
|
||||||
|
+echo
|
||||||
|
+
|
||||||
|
+_launch_qemu -machine q35 \
|
||||||
|
+ -object iothread,id=iothread0 \
|
||||||
|
+ -object "${tls_obj_base}"/server1,endpoint=server \
|
||||||
|
+ -device '{"driver":"pcie-root-port", "id":"root0", "multifunction":true,
|
||||||
|
+ "bus":"pcie.0"}' \
|
||||||
|
+ -device '{"driver":"virtio-scsi-pci", "id":"virtio_scsi_pci0",
|
||||||
|
+ "bus":"root0", "iothread":"iothread0"}' \
|
||||||
|
+ -device '{"driver":"scsi-hd", "id":"image1", "drive":"drive_image1",
|
||||||
|
+ "bus":"virtio_scsi_pci0.0"}' \
|
||||||
|
+ -blockdev '{"driver":"file", "cache":{"direct":true, "no-flush":false},
|
||||||
|
+ "filename":"'"$DST_IMG"'", "node-name":"drive_sys1"}' \
|
||||||
|
+ -blockdev '{"driver":"qcow2", "node-name":"drive_image1",
|
||||||
|
+ "file":"drive_sys1"}' \
|
||||||
|
+ -incoming defer
|
||||||
|
+h2=$QEMU_HANDLE
|
||||||
|
+_send_qemu_cmd $h2 '{"execute": "qmp_capabilities"}' 'return'
|
||||||
|
+
|
||||||
|
+echo
|
||||||
|
+echo === Dst VM: Enable NBD server for incoming storage migration ===
|
||||||
|
+echo
|
||||||
|
+
|
||||||
|
+_send_qemu_cmd $h2 '{"execute": "nbd-server-start", "arguments":
|
||||||
|
+ {"addr": {"type": "inet", "data": {"host": "127.0.0.1", "port": "'$port'"}},
|
||||||
|
+ "tls-creds": "tls0"}}' '{"return": {}}' | sed "s/\"$port\"/PORT/g"
|
||||||
|
+_send_qemu_cmd $h2 '{"execute": "block-export-add", "arguments":
|
||||||
|
+ {"node-name": "drive_image1", "type": "nbd", "writable": true,
|
||||||
|
+ "id": "drive_image1"}}' '{"return": {}}'
|
||||||
|
+
|
||||||
|
+echo
|
||||||
|
+echo === Src VM: Mirror to dst NBD for outgoing storage migration ===
|
||||||
|
+echo
|
||||||
|
+
|
||||||
|
+_send_qemu_cmd $h1 '{"execute": "blockdev-add", "arguments":
|
||||||
|
+ {"node-name": "mirror", "driver": "nbd",
|
||||||
|
+ "server": {"type": "inet", "host": "127.0.0.1", "port": "'$port'"},
|
||||||
|
+ "export": "drive_image1", "tls-creds": "tls0"}}' '{"return": {}}' | sed "s/\"$port\"/PORT/g"
|
||||||
|
+_send_qemu_cmd $h1 '{"execute": "blockdev-mirror", "arguments":
|
||||||
|
+ {"sync": "full", "device": "drive_image1", "target": "mirror",
|
||||||
|
+ "job-id": "drive_image1_53"}}' '{"return": {}}'
|
||||||
|
+_timed_wait_for $h1 '"ready"'
|
||||||
|
+
|
||||||
|
+echo
|
||||||
|
+echo === Cleaning up ===
|
||||||
|
+echo
|
||||||
|
+
|
||||||
|
+_send_qemu_cmd $h1 '{"execute":"quit"}' ''
|
||||||
|
+_send_qemu_cmd $h2 '{"execute":"quit"}' ''
|
||||||
|
+
|
||||||
|
+echo "*** done"
|
||||||
|
+rm -f $seq.full
|
||||||
|
+status=0
|
||||||
|
diff --git a/tests/qemu-iotests/tests/nbd-tls-iothread.out b/tests/qemu-iotests/tests/nbd-tls-iothread.out
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..a3899fd2d7
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/tests/qemu-iotests/tests/nbd-tls-iothread.out
|
||||||
|
@@ -0,0 +1,53 @@
|
||||||
|
+QA output created by nbd-tls-iothread
|
||||||
|
+
|
||||||
|
+== preparing TLS creds and spare port ==
|
||||||
|
+picked unused port
|
||||||
|
+Generating a self signed certificate...
|
||||||
|
+Generating a signed certificate...
|
||||||
|
+Generating a signed certificate...
|
||||||
|
+
|
||||||
|
+== preparing image ==
|
||||||
|
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
+Formatting 'TEST_DIR/dst.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
+
|
||||||
|
+=== Starting Src QEMU ===
|
||||||
|
+
|
||||||
|
+{"execute": "qmp_capabilities"}
|
||||||
|
+{"return": {}}
|
||||||
|
+
|
||||||
|
+=== Starting Dst VM2 ===
|
||||||
|
+
|
||||||
|
+{"execute": "qmp_capabilities"}
|
||||||
|
+{"return": {}}
|
||||||
|
+
|
||||||
|
+=== Dst VM: Enable NBD server for incoming storage migration ===
|
||||||
|
+
|
||||||
|
+{"execute": "nbd-server-start", "arguments":
|
||||||
|
+ {"addr": {"type": "inet", "data": {"host": "127.0.0.1", "port": PORT}},
|
||||||
|
+ "tls-creds": "tls0"}}
|
||||||
|
+{"return": {}}
|
||||||
|
+{"execute": "block-export-add", "arguments":
|
||||||
|
+ {"node-name": "drive_image1", "type": "nbd", "writable": true,
|
||||||
|
+ "id": "drive_image1"}}
|
||||||
|
+{"return": {}}
|
||||||
|
+
|
||||||
|
+=== Src VM: Mirror to dst NBD for outgoing storage migration ===
|
||||||
|
+
|
||||||
|
+{"execute": "blockdev-add", "arguments":
|
||||||
|
+ {"node-name": "mirror", "driver": "nbd",
|
||||||
|
+ "server": {"type": "inet", "host": "127.0.0.1", "port": PORT},
|
||||||
|
+ "export": "drive_image1", "tls-creds": "tls0"}}
|
||||||
|
+{"return": {}}
|
||||||
|
+{"execute": "blockdev-mirror", "arguments":
|
||||||
|
+ {"sync": "full", "device": "drive_image1", "target": "mirror",
|
||||||
|
+ "job-id": "drive_image1_53"}}
|
||||||
|
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "drive_image1_53"}}
|
||||||
|
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "drive_image1_53"}}
|
||||||
|
+{"return": {}}
|
||||||
|
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "drive_image1_53"}}
|
||||||
|
+
|
||||||
|
+=== Cleaning up ===
|
||||||
|
+
|
||||||
|
+{"execute":"quit"}
|
||||||
|
+{"execute":"quit"}
|
||||||
|
+*** done
|
||||||
|
--
|
||||||
|
2.39.3
|
||||||
|
|
@ -0,0 +1,101 @@
|
|||||||
|
From 676438ff8c42323c3e5d9e7eeeb1b3367999136c Mon Sep 17 00:00:00 2001
|
||||||
|
From: Eric Blake <eblake@redhat.com>
|
||||||
|
Date: Thu, 22 Aug 2024 09:35:29 -0500
|
||||||
|
Subject: [PATCH 3/3] nbd/server: CVE-2024-7409: Avoid use-after-free when
|
||||||
|
closing server
|
||||||
|
|
||||||
|
RH-Author: Eric Blake <eblake@redhat.com>
|
||||||
|
RH-MergeRequest: 398: nbd/server: CVE-2024-7409: Avoid use-after-free when closing server
|
||||||
|
RH-Jira: RHEL-52611
|
||||||
|
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
RH-Commit: [3/3] 1ee35a40ded067a085bf6fcafa690b40976d7f2d (ebblake/qemu-kvm)
|
||||||
|
|
||||||
|
Commit 3e7ef738 plugged the use-after-free of the global nbd_server
|
||||||
|
object, but overlooked a use-after-free of nbd_server->listener.
|
||||||
|
Although this race is harder to hit, notice that our shutdown path
|
||||||
|
first drops the reference count of nbd_server->listener, then triggers
|
||||||
|
actions that can result in a pending client reaching the
|
||||||
|
nbd_blockdev_client_closed() callback, which in turn calls
|
||||||
|
qio_net_listener_set_client_func on a potentially stale object.
|
||||||
|
|
||||||
|
If we know we don't want any more clients to connect, and have already
|
||||||
|
told the listener socket to shut down, then we should not be trying to
|
||||||
|
update the listener socket's associated function.
|
||||||
|
|
||||||
|
Reproducer:
|
||||||
|
|
||||||
|
> #!/usr/bin/python3
|
||||||
|
>
|
||||||
|
> import os
|
||||||
|
> from threading import Thread
|
||||||
|
>
|
||||||
|
> def start_stop():
|
||||||
|
> while 1:
|
||||||
|
> os.system('virsh qemu-monitor-command VM \'{"execute": "nbd-server-start",
|
||||||
|
+"arguments":{"addr":{"type":"unix","data":{"path":"/tmp/nbd-sock"}}}}\'')
|
||||||
|
> os.system('virsh qemu-monitor-command VM \'{"execute": "nbd-server-stop"}\'')
|
||||||
|
>
|
||||||
|
> def nbd_list():
|
||||||
|
> while 1:
|
||||||
|
> os.system('/path/to/build/qemu-nbd -L -k /tmp/nbd-sock')
|
||||||
|
>
|
||||||
|
> def test():
|
||||||
|
> sst = Thread(target=start_stop)
|
||||||
|
> sst.start()
|
||||||
|
> nlt = Thread(target=nbd_list)
|
||||||
|
> nlt.start()
|
||||||
|
>
|
||||||
|
> sst.join()
|
||||||
|
> nlt.join()
|
||||||
|
>
|
||||||
|
> test()
|
||||||
|
|
||||||
|
Fixes: CVE-2024-7409
|
||||||
|
Fixes: 3e7ef738c8 ("nbd/server: CVE-2024-7409: Close stray clients at server-stop")
|
||||||
|
CC: qemu-stable@nongnu.org
|
||||||
|
Reported-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
|
||||||
|
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||||
|
Message-ID: <20240822143617.800419-2-eblake@redhat.com>
|
||||||
|
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
|
||||||
|
(cherry picked from commit 3874f5f73c441c52f1c699c848d463b0eda01e4c)
|
||||||
|
Jira: https://issues.redhat.com/browse/RHEL-52611
|
||||||
|
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||||
|
---
|
||||||
|
blockdev-nbd.c | 12 ++++++++----
|
||||||
|
1 file changed, 8 insertions(+), 4 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
|
||||||
|
index 87839c180b..b5d55e2518 100644
|
||||||
|
--- a/blockdev-nbd.c
|
||||||
|
+++ b/blockdev-nbd.c
|
||||||
|
@@ -87,10 +87,13 @@ static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc,
|
||||||
|
|
||||||
|
static void nbd_update_server_watch(NBDServerData *s)
|
||||||
|
{
|
||||||
|
- if (!s->max_connections || s->connections < s->max_connections) {
|
||||||
|
- qio_net_listener_set_client_func(s->listener, nbd_accept, NULL, NULL);
|
||||||
|
- } else {
|
||||||
|
- qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
|
||||||
|
+ if (s->listener) {
|
||||||
|
+ if (!s->max_connections || s->connections < s->max_connections) {
|
||||||
|
+ qio_net_listener_set_client_func(s->listener, nbd_accept, NULL,
|
||||||
|
+ NULL);
|
||||||
|
+ } else {
|
||||||
|
+ qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -108,6 +111,7 @@ static void nbd_server_free(NBDServerData *server)
|
||||||
|
*/
|
||||||
|
qio_net_listener_disconnect(server->listener);
|
||||||
|
object_unref(OBJECT(server->listener));
|
||||||
|
+ server->listener = NULL;
|
||||||
|
QLIST_FOREACH_SAFE(conn, &server->conns, next, tmp) {
|
||||||
|
qio_channel_shutdown(QIO_CHANNEL(conn->cioc), QIO_CHANNEL_SHUTDOWN_BOTH,
|
||||||
|
NULL);
|
||||||
|
--
|
||||||
|
2.39.3
|
||||||
|
|
@ -0,0 +1,161 @@
|
|||||||
|
From 00af174d1388ed2d2df7961ee78be6af3757a01c Mon Sep 17 00:00:00 2001
|
||||||
|
From: Eric Blake <eblake@redhat.com>
|
||||||
|
Date: Wed, 30 Aug 2023 18:48:02 -0400
|
||||||
|
Subject: [PATCH 1/3] nbd/server: Favor qemu_aio_context over iohandler context
|
||||||
|
|
||||||
|
RH-Author: Eric Blake <eblake@redhat.com>
|
||||||
|
RH-MergeRequest: 398: nbd/server: CVE-2024-7409: Avoid use-after-free when closing server
|
||||||
|
RH-Jira: RHEL-52611
|
||||||
|
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
RH-Commit: [1/3] 6ec0ef287fbc976175da83a0c14d9878e83affa2 (ebblake/qemu-kvm)
|
||||||
|
|
||||||
|
DOWNSTREAM ONLY - but based on an idea originally included as a
|
||||||
|
side-effect in the larger upstream patch 06e0f098 "io: follow
|
||||||
|
coroutine AioContext in qio_channel_yield()", as well as handling the
|
||||||
|
state of the qio TLS channel before it is associated with a block
|
||||||
|
device as an alternative to 199e84de "qio: Inherit
|
||||||
|
follow_coroutine_ctx across TLS".
|
||||||
|
|
||||||
|
The NBD server code wants to use qio_channel_shutdown() followed by
|
||||||
|
AIO_WAIT_WHILE() during nbd_server_free(), but cannot attach the ioc
|
||||||
|
to an AioContext until the client has completed the handshake to the
|
||||||
|
point that the server knows what block device to associate with the
|
||||||
|
connection. The qio code is set up to handle connections with no
|
||||||
|
AioContext in the iohandler context, but this context is specifically
|
||||||
|
designed to NOT make progress during AIO_WAIT_WHILE(). In order to
|
||||||
|
prevent things from deadlocking, the qio channels handling NBD
|
||||||
|
handshake MUST be in the qemu_aio_context, so that an early shutdown
|
||||||
|
triggered by nbd-server-stop can make progress.
|
||||||
|
|
||||||
|
Note that upstream handled the main qio channel by the use of
|
||||||
|
qio_channel_set_follow_coroutine_ctx() in only one place in
|
||||||
|
nbd/server.c; upstream handled the TLS channel by a more generic
|
||||||
|
second patch that taught qio TLS channel to inherit the
|
||||||
|
follow_coroutine_ctx status from its parent. But since this patch is
|
||||||
|
already downstream only, the minimal diff is achieved by manually
|
||||||
|
setting the status of the TLS channel in NBD code, rather than
|
||||||
|
backporting the qio inheritance code. For testing that the second
|
||||||
|
call to qio_channel_set_favor_qemu_aio_ctx() matters, I used this test
|
||||||
|
setup (borrowing a pre-built PSK file for username alice from the
|
||||||
|
libnbd project, and using IPv4 since this qemu is too old to support
|
||||||
|
TLS over Unix sockets):
|
||||||
|
|
||||||
|
$ # in terminal 1:
|
||||||
|
$ qemu-system-x86_64 --nographic --nodefaults --qmp stdio \
|
||||||
|
--object tls-creds-psk,id=tls0,dir=/PATHTO/libnbd/tests,endpoint=server
|
||||||
|
{"execute": "qmp_capabilities"}
|
||||||
|
{"execute":"nbd-server-start","arguments":{"addr":{"type":"inet",
|
||||||
|
"data":{"host":"127.0.0.1","port":"10809"}},"tls-creds":"tls0"}}
|
||||||
|
|
||||||
|
$ # in terminal 2:
|
||||||
|
$ nbdsh -c 'h.set_uri_allow_local_file(True)' --opt-mode -u \
|
||||||
|
'nbds://alice@127.0.0.1/?tls-psk-file=/PATHTO/libnbd/tests/keys.psk' \
|
||||||
|
-c 'import time; time.sleep(15)'
|
||||||
|
|
||||||
|
$ # in terminal 1, before 10 seconds elapse
|
||||||
|
{"execute":"nbd-server-stop"}
|
||||||
|
{"execute":"quit"}
|
||||||
|
|
||||||
|
and observed that, when omitting the one-line TLS setting, qemu would
|
||||||
|
hit the same deadlock with a TLS client as what I was observing for a
|
||||||
|
non-TLS client without this entire patch.
|
||||||
|
|
||||||
|
Jira: https://issues.redhat.com/browse/RHEL-52611
|
||||||
|
Suggested-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||||
|
---
|
||||||
|
include/io/channel.h | 16 ++++++++++++++++
|
||||||
|
io/channel.c | 14 +++++++++++++-
|
||||||
|
nbd/server.c | 2 ++
|
||||||
|
3 files changed, 31 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/include/io/channel.h b/include/io/channel.h
|
||||||
|
index 716235d496..f1ce19ea81 100644
|
||||||
|
--- a/include/io/channel.h
|
||||||
|
+++ b/include/io/channel.h
|
||||||
|
@@ -84,6 +84,7 @@ struct QIOChannel {
|
||||||
|
AioContext *ctx;
|
||||||
|
Coroutine *read_coroutine;
|
||||||
|
Coroutine *write_coroutine;
|
||||||
|
+ bool favor_qemu_aio_ctx;
|
||||||
|
#ifdef _WIN32
|
||||||
|
HANDLE event; /* For use with GSource on Win32 */
|
||||||
|
#endif
|
||||||
|
@@ -498,6 +499,21 @@ int qio_channel_set_blocking(QIOChannel *ioc,
|
||||||
|
bool enabled,
|
||||||
|
Error **errp);
|
||||||
|
|
||||||
|
+/**
|
||||||
|
+ * qio_channel_set_favor_qemu_aio_ctx:
|
||||||
|
+ * @ioc: the channel object
|
||||||
|
+ * @enabled: whether to fall back to qemu_aio_context
|
||||||
|
+ *
|
||||||
|
+ * If @enabled is true, calls to qio_channel_yield() with no AioContext
|
||||||
|
+ * set use the qemu_aio_context instead of the global iohandler context.
|
||||||
|
+ *
|
||||||
|
+ * If @enabled is false, calls to qio_channel_yield() use the global iohandler
|
||||||
|
+ * AioContext. This is may be used by coroutines that run in the main loop and
|
||||||
|
+ * do not wish to respond to I/O during nested event loops. This is the
|
||||||
|
+ * default for compatibility with code that is not aware of AioContexts.
|
||||||
|
+ */
|
||||||
|
+void qio_channel_set_favor_qemu_aio_ctx(QIOChannel *ioc, bool enabled);
|
||||||
|
+
|
||||||
|
/**
|
||||||
|
* qio_channel_close:
|
||||||
|
* @ioc: the channel object
|
||||||
|
diff --git a/io/channel.c b/io/channel.c
|
||||||
|
index a8c7f11649..74704d0464 100644
|
||||||
|
--- a/io/channel.c
|
||||||
|
+++ b/io/channel.c
|
||||||
|
@@ -364,6 +364,12 @@ int qio_channel_set_blocking(QIOChannel *ioc,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
+void qio_channel_set_favor_qemu_aio_ctx(QIOChannel *ioc, bool enabled)
|
||||||
|
+{
|
||||||
|
+ ioc->favor_qemu_aio_ctx = enabled;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
int qio_channel_close(QIOChannel *ioc,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
@@ -545,7 +551,13 @@ static void qio_channel_set_aio_fd_handlers(QIOChannel *ioc)
|
||||||
|
wr_handler = qio_channel_restart_write;
|
||||||
|
}
|
||||||
|
|
||||||
|
- ctx = ioc->ctx ? ioc->ctx : iohandler_get_aio_context();
|
||||||
|
+ if (ioc->ctx) {
|
||||||
|
+ ctx = ioc->ctx;
|
||||||
|
+ } else if (ioc->favor_qemu_aio_ctx) {
|
||||||
|
+ ctx = qemu_get_aio_context();
|
||||||
|
+ } else {
|
||||||
|
+ ctx = iohandler_get_aio_context();
|
||||||
|
+ }
|
||||||
|
qio_channel_set_aio_fd_handler(ioc, ctx, rd_handler, wr_handler, ioc);
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/nbd/server.c b/nbd/server.c
|
||||||
|
index 1265068f70..41a2003300 100644
|
||||||
|
--- a/nbd/server.c
|
||||||
|
+++ b/nbd/server.c
|
||||||
|
@@ -758,6 +758,7 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client,
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ qio_channel_set_favor_qemu_aio_ctx(QIO_CHANNEL(tioc), true);
|
||||||
|
qio_channel_set_name(QIO_CHANNEL(tioc), "nbd-server-tls");
|
||||||
|
trace_nbd_negotiate_handle_starttls_handshake();
|
||||||
|
data.loop = g_main_loop_new(g_main_context_default(), FALSE);
|
||||||
|
@@ -1333,6 +1334,7 @@ static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp)
|
||||||
|
*/
|
||||||
|
|
||||||
|
qio_channel_set_blocking(client->ioc, false, NULL);
|
||||||
|
+ qio_channel_set_favor_qemu_aio_ctx(client->ioc, true);
|
||||||
|
|
||||||
|
trace_nbd_negotiate_begin();
|
||||||
|
memcpy(buf, "NBDMAGIC", 8);
|
||||||
|
--
|
||||||
|
2.39.3
|
||||||
|
|
Loading…
Reference in new issue