From b8753d7e41520928b3bf125367efce2d37fac82a Mon Sep 17 00:00:00 2001 From: eabdullin Date: Wed, 4 Sep 2024 05:31:51 +0000 Subject: [PATCH] import OL qemu-kvm-8.2.0-11.el9_4.6 --- ...canout-version-with-pc-q35-rhel9.4.0.patch | 128 ++++++++++++ ...io-add-IO_CMD_FDSYNC-command-support.patch | 126 +++++++++++ ...024-7409-Cap-default-max-connections.patch | 184 ++++++++++++++++ ...024-7409-Close-stray-clients-at-serv.patch | 173 ++++++++++++++++ ...024-7409-Drop-non-negotiating-client.patch | 134 ++++++++++++ ...-Plumb-in-new-args-to-nbd_client_add.patch | 175 ++++++++++++++++ ...ne-type-compat-for-virtio-gpu-migrat.patch | 38 ++++ ...-gpu-fix-scanout-migration-post-load.patch | 196 ++++++++++++++++++ SOURCES/kvm-virtio-gpu-fix-v2-migration.patch | 122 +++++++++++ SPECS/qemu-kvm.spec | 41 +++- 10 files changed, 1316 insertions(+), 1 deletion(-) create mode 100644 SOURCES/kvm-Fix-scanout-version-with-pc-q35-rhel9.4.0.patch create mode 100644 SOURCES/kvm-linux-aio-add-IO_CMD_FDSYNC-command-support.patch create mode 100644 SOURCES/kvm-nbd-server-CVE-2024-7409-Cap-default-max-connections.patch create mode 100644 SOURCES/kvm-nbd-server-CVE-2024-7409-Close-stray-clients-at-serv.patch create mode 100644 SOURCES/kvm-nbd-server-CVE-2024-7409-Drop-non-negotiating-client.patch create mode 100644 SOURCES/kvm-nbd-server-Plumb-in-new-args-to-nbd_client_add.patch create mode 100644 SOURCES/kvm-rhel-9.4.0-machine-type-compat-for-virtio-gpu-migrat.patch create mode 100644 SOURCES/kvm-virtio-gpu-fix-scanout-migration-post-load.patch create mode 100644 SOURCES/kvm-virtio-gpu-fix-v2-migration.patch diff --git a/SOURCES/kvm-Fix-scanout-version-with-pc-q35-rhel9.4.0.patch b/SOURCES/kvm-Fix-scanout-version-with-pc-q35-rhel9.4.0.patch new file mode 100644 index 0000000..fdac70c --- /dev/null +++ b/SOURCES/kvm-Fix-scanout-version-with-pc-q35-rhel9.4.0.patch @@ -0,0 +1,128 @@ +From ef212eb3026a2460f6502a76afa3baedeb74d975 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= +Date: Tue, 6 Aug 2024 14:14:27 +0400 +Subject: [PATCH 1/5] Fix scanout version with pc-q35-rhel9.4.0 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Marc-André Lureau +RH-MergeRequest: 383: Fix scanout version with pc-q35-rhel9.4.0 +RH-Jira: RHEL-53565 +RH-Acked-by: Miroslav Rezanina +RH-Commit: [1/1] ed2860cfb933654b0046c1b59f78d2e50146bd6c + +Resolves: https://issues.redhat.com/browse/RHEL-52940 + +Introduce hw_compat_rhel_9_4_extra so that pc-q35-rhel9.4.0 has +x-scanout-vmstate-version=1 + +Signed-off-by: Marc-André Lureau +--- + hw/arm/virt.c | 1 + + hw/core/machine.c | 13 +++++++++++-- + hw/i386/pc_piix.c | 2 ++ + hw/i386/pc_q35.c | 3 +++ + hw/s390x/s390-virtio-ccw.c | 1 + + include/hw/boards.h | 3 +++ + 6 files changed, 21 insertions(+), 2 deletions(-) + +diff --git a/hw/arm/virt.c b/hw/arm/virt.c +index e4a66affcb..851eff6b28 100644 +--- a/hw/arm/virt.c ++++ b/hw/arm/virt.c +@@ -3670,6 +3670,7 @@ type_init(rhel_machine_init); + + static void rhel940_virt_options(MachineClass *mc) + { ++ compat_props_add(mc->compat_props, hw_compat_rhel_9_4_extra, hw_compat_rhel_9_4_extra_len); + } + DEFINE_RHEL_MACHINE_AS_LATEST(9, 4, 0) + +diff --git a/hw/core/machine.c b/hw/core/machine.c +index c8c460c916..fe0a28ef3b 100644 +--- a/hw/core/machine.c ++++ b/hw/core/machine.c +@@ -64,6 +64,17 @@ const size_t hw_compat_7_2_len = G_N_ELEMENTS(hw_compat_7_2); + const char *rhel_old_machine_deprecation = + "machine types for previous major releases are deprecated"; + ++/* ++ * rhel_9_4_extra is used for <= 9.4 machines. ++ * ++ * (for compatibility and historical reasons, rhel_9_4 is used for <= 9.2 too) ++ */ ++GlobalProperty hw_compat_rhel_9_4_extra[] = { ++ /* hw_compat_rhel_9_4_extra from hw_compat_8_2 */ ++ { "virtio-gpu-device", "x-scanout-vmstate-version", "1" }, ++}; ++const size_t hw_compat_rhel_9_4_extra_len = G_N_ELEMENTS(hw_compat_rhel_9_4_extra); ++ + GlobalProperty hw_compat_rhel_9_4[] = { + /* hw_compat_rhel_9_4 from hw_compat_8_0 */ + { TYPE_VIRTIO_NET, "host_uso", "off"}, +@@ -81,8 +92,6 @@ GlobalProperty hw_compat_rhel_9_4[] = { + { "igb", "x-pcie-flr-init", "off" }, + /* hw_compat_rhel_9_4 jira RHEL-24045 */ + { "virtio-mem", "dynamic-memslots", "off" }, +- /* hw_compat_rhel_9_4 from hw_compat_8_1 */ +- { "virtio-gpu-device", "x-scanout-vmstate-version", "1" }, + }; + const size_t hw_compat_rhel_9_4_len = G_N_ELEMENTS(hw_compat_rhel_9_4); + +diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c +index 54d1c58bce..c846673d87 100644 +--- a/hw/i386/pc_piix.c ++++ b/hw/i386/pc_piix.c +@@ -1031,6 +1031,8 @@ static void pc_machine_rhel760_options(MachineClass *m) + "Use a different south bridge than PIIX3"); + + ++ compat_props_add(m->compat_props, hw_compat_rhel_9_4_extra, ++ hw_compat_rhel_9_4_extra_len); + compat_props_add(m->compat_props, hw_compat_rhel_9_4, + hw_compat_rhel_9_4_len); + compat_props_add(m->compat_props, hw_compat_rhel_9_3, +diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c +index cd5fb7380e..02bc3d515f 100644 +--- a/hw/i386/pc_q35.c ++++ b/hw/i386/pc_q35.c +@@ -731,6 +731,9 @@ static void pc_q35_machine_rhel940_options(MachineClass *m) + m->desc = "RHEL-9.4.0 PC (Q35 + ICH9, 2009)"; + pcmc->smbios_stream_product = "RHEL"; + pcmc->smbios_stream_version = "9.4.0"; ++ ++ compat_props_add(m->compat_props, hw_compat_rhel_9_4_extra, ++ hw_compat_rhel_9_4_extra_len); + } + + DEFINE_PC_MACHINE(q35_rhel940, "pc-q35-rhel9.4.0", pc_q35_init_rhel940, +diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c +index 24f4773179..cc6087c37a 100644 +--- a/hw/s390x/s390-virtio-ccw.c ++++ b/hw/s390x/s390-virtio-ccw.c +@@ -1277,6 +1277,7 @@ static void ccw_machine_rhel940_instance_options(MachineState *machine) + + static void ccw_machine_rhel940_class_options(MachineClass *mc) + { ++ compat_props_add(mc->compat_props, hw_compat_rhel_9_4_extra, hw_compat_rhel_9_4_extra_len); + } + DEFINE_CCW_MACHINE(rhel940, "rhel9.4.0", true); + +diff --git a/include/hw/boards.h b/include/hw/boards.h +index 4edfdb0ddb..e6ae0e61cf 100644 +--- a/include/hw/boards.h ++++ b/include/hw/boards.h +@@ -505,6 +505,9 @@ extern const size_t hw_compat_2_2_len; + extern GlobalProperty hw_compat_2_1[]; + extern const size_t hw_compat_2_1_len; + ++extern GlobalProperty hw_compat_rhel_9_4_extra[]; ++extern const size_t hw_compat_rhel_9_4_extra_len; ++ + extern GlobalProperty hw_compat_rhel_9_4[]; + extern const size_t hw_compat_rhel_9_4_len; + +-- +2.39.3 + diff --git a/SOURCES/kvm-linux-aio-add-IO_CMD_FDSYNC-command-support.patch b/SOURCES/kvm-linux-aio-add-IO_CMD_FDSYNC-command-support.patch new file mode 100644 index 0000000..391e618 --- /dev/null +++ b/SOURCES/kvm-linux-aio-add-IO_CMD_FDSYNC-command-support.patch @@ -0,0 +1,126 @@ +From 9d00965910d5a7818ffe55b18d3e9dd4c7210e1b Mon Sep 17 00:00:00 2001 +From: Prasad Pandit +Date: Thu, 25 Apr 2024 12:34:12 +0530 +Subject: [PATCH 4/4] linux-aio: add IO_CMD_FDSYNC command support + +RH-Author: Prasad Pandit +RH-MergeRequest: 378: linux-aio: add IO_CMD_FDSYNC command support +RH-Jira: RHEL-43261 +RH-Acked-by: Miroslav Rezanina +RH-Commit: [1/1] 3b80d4a162aad1a87322078a7d7b060a9496035b + +Libaio defines IO_CMD_FDSYNC command to sync all outstanding +asynchronous I/O operations, by flushing out file data to the +disk storage. Enable linux-aio to submit such aio request. + +When using aio=native without fdsync() support, QEMU creates +pthreads, and destroying these pthreads results in TLB flushes. +In a real-time guest environment, TLB flushes cause a latency +spike. This patch helps to avoid such spikes. + +Jira: https://issues.redhat.com/browse/RHEL-43261 +Reviewed-by: Stefan Hajnoczi +Signed-off-by: Prasad Pandit +Message-ID: <20240425070412.37248-1-ppandit@redhat.com> +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit 24687abf237e3c15816d689a8e4b08d7c3190dcb) +Signed-off-by: Prasad Pandit +--- + block/file-posix.c | 9 +++++++++ + block/linux-aio.c | 21 ++++++++++++++++++++- + include/block/raw-aio.h | 1 + + 3 files changed, 30 insertions(+), 1 deletion(-) + +diff --git a/block/file-posix.c b/block/file-posix.c +index 35684f7e21..9831b08fb6 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -159,6 +159,7 @@ typedef struct BDRVRawState { + bool has_discard:1; + bool has_write_zeroes:1; + bool use_linux_aio:1; ++ bool has_laio_fdsync:1; + bool use_linux_io_uring:1; + int page_cache_inconsistent; /* errno from fdatasync failure */ + bool has_fallocate; +@@ -718,6 +719,9 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, + ret = -EINVAL; + goto fail; + } ++ if (s->use_linux_aio) { ++ s->has_laio_fdsync = laio_has_fdsync(s->fd); ++ } + #else + if (s->use_linux_aio) { + error_setg(errp, "aio=native was specified, but is not supported " +@@ -2599,6 +2603,11 @@ static int coroutine_fn raw_co_flush_to_disk(BlockDriverState *bs) + if (raw_check_linux_io_uring(s)) { + return luring_co_submit(bs, s->fd, 0, NULL, QEMU_AIO_FLUSH); + } ++#endif ++#ifdef CONFIG_LINUX_AIO ++ if (s->has_laio_fdsync && raw_check_linux_aio(s)) { ++ return laio_co_submit(s->fd, 0, NULL, QEMU_AIO_FLUSH, 0); ++ } + #endif + return raw_thread_pool_submit(handle_aiocb_flush, &acb); + } +diff --git a/block/linux-aio.c b/block/linux-aio.c +index ec05d946f3..e3b5ec9aba 100644 +--- a/block/linux-aio.c ++++ b/block/linux-aio.c +@@ -384,6 +384,9 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset, + case QEMU_AIO_READ: + io_prep_preadv(iocbs, fd, qiov->iov, qiov->niov, offset); + break; ++ case QEMU_AIO_FLUSH: ++ io_prep_fdsync(iocbs, fd); ++ break; + /* Currently Linux kernel does not support other operations */ + default: + fprintf(stderr, "%s: invalid AIO request type 0x%x.\n", +@@ -412,7 +415,7 @@ int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov, + AioContext *ctx = qemu_get_current_aio_context(); + struct qemu_laiocb laiocb = { + .co = qemu_coroutine_self(), +- .nbytes = qiov->size, ++ .nbytes = qiov ? qiov->size : 0, + .ctx = aio_get_linux_aio(ctx), + .ret = -EINPROGRESS, + .is_read = (type == QEMU_AIO_READ), +@@ -486,3 +489,19 @@ void laio_cleanup(LinuxAioState *s) + } + g_free(s); + } ++ ++bool laio_has_fdsync(int fd) ++{ ++ struct iocb cb; ++ struct iocb *cbs[] = {&cb, NULL}; ++ ++ io_context_t ctx = 0; ++ io_setup(1, &ctx); ++ ++ /* check if host kernel supports IO_CMD_FDSYNC */ ++ io_prep_fdsync(&cb, fd); ++ int ret = io_submit(ctx, 1, cbs); ++ ++ io_destroy(ctx); ++ return (ret == -EINVAL) ? false : true; ++} +diff --git a/include/block/raw-aio.h b/include/block/raw-aio.h +index 0f63c2800c..3166903a56 100644 +--- a/include/block/raw-aio.h ++++ b/include/block/raw-aio.h +@@ -60,6 +60,7 @@ void laio_cleanup(LinuxAioState *s); + int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov, + int type, uint64_t dev_max_batch); + ++bool laio_has_fdsync(int); + void laio_detach_aio_context(LinuxAioState *s, AioContext *old_context); + void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context); + #endif +-- +2.39.3 + diff --git a/SOURCES/kvm-nbd-server-CVE-2024-7409-Cap-default-max-connections.patch b/SOURCES/kvm-nbd-server-CVE-2024-7409-Cap-default-max-connections.patch new file mode 100644 index 0000000..7098ffc --- /dev/null +++ b/SOURCES/kvm-nbd-server-CVE-2024-7409-Cap-default-max-connections.patch @@ -0,0 +1,184 @@ +From 3311ddaffa78ad8d2c40c86cd69461ebeabe1052 Mon Sep 17 00:00:00 2001 +From: Eric Blake +Date: Tue, 6 Aug 2024 13:53:00 -0500 +Subject: [PATCH 3/5] nbd/server: CVE-2024-7409: Cap default max-connections to + 100 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Eric Blake +RH-MergeRequest: 386: nbd/server: fix CVE-2024-7409 (qemu crash on nbd-server-stop) [rhel-9.4.z] +RH-Jira: RHEL-52616 +RH-Acked-by: Miroslav Rezanina +RH-Commit: [2/4] 17c9c81282e180485e2f2d584a3e0c73a6fbd66a (ebblake/qemu-kvm) + +Allowing an unlimited number of clients to any web service is a recipe +for a rudimentary denial of service attack: the client merely needs to +open lots of sockets without closing them, until qemu no longer has +any more fds available to allocate. + +For qemu-nbd, we default to allowing only 1 connection unless more are +explicitly asked for (-e or --shared); this was historically picked as +a nice default (without an explicit -t, a non-persistent qemu-nbd goes +away after a client disconnects, without needing any additional +follow-up commands), and we are not going to change that interface now +(besides, someday we want to point people towards qemu-storage-daemon +instead of qemu-nbd). + +But for qemu proper, and the newer qemu-storage-daemon, the QMP +nbd-server-start command has historically had a default of unlimited +number of connections, in part because unlike qemu-nbd it is +inherently persistent until nbd-server-stop. Allowing multiple client +sockets is particularly useful for clients that can take advantage of +MULTI_CONN (creating parallel sockets to increase throughput), +although known clients that do so (such as libnbd's nbdcopy) typically +use only 8 or 16 connections (the benefits of scaling diminish once +more sockets are competing for kernel attention). Picking a number +large enough for typical use cases, but not unlimited, makes it +slightly harder for a malicious client to perform a denial of service +merely by opening lots of connections withot progressing through the +handshake. + +This change does not eliminate CVE-2024-7409 on its own, but reduces +the chance for fd exhaustion or unlimited memory usage as an attack +surface. On the other hand, by itself, it makes it more obvious that +with a finite limit, we have the problem of an unauthenticated client +holding 100 fds opened as a way to block out a legitimate client from +being able to connect; thus, later patches will further add timeouts +to reject clients that are not making progress. + +This is an INTENTIONAL change in behavior, and will break any client +of nbd-server-start that was not passing an explicit max-connections +parameter, yet expects more than 100 simultaneous connections. We are +not aware of any such client (as stated above, most clients aware of +MULTI_CONN get by just fine on 8 or 16 connections, and probably cope +with later connections failing by relying on the earlier connections; +libvirt has not yet been passing max-connections, but generally +creates NBD servers with the intent for a single client for the sake +of live storage migration; meanwhile, the KubeSAN project anticipates +a large cluster sharing multiple clients [up to 8 per node, and up to +100 nodes in a cluster], but it currently uses qemu-nbd with an +explicit --shared=0 rather than qemu-storage-daemon with +nbd-server-start). + +We considered using a deprecation period (declare that omitting +max-parameters is deprecated, and make it mandatory in 3 releases - +then we don't need to pick an arbitrary default); that has zero risk +of breaking any apps that accidentally depended on more than 100 +connections, and where such breakage might not be noticed under unit +testing but only under the larger loads of production usage. But it +does not close the denial-of-service hole until far into the future, +and requires all apps to change to add the parameter even if 100 was +good enough. It also has a drawback that any app (like libvirt) that +is accidentally relying on an unlimited default should seriously +consider their own CVE now, at which point they are going to change to +pass explicit max-connections sooner than waiting for 3 qemu releases. +Finally, if our changed default breaks an app, that app can always +pass in an explicit max-parameters with a larger value. + +It is also intentional that the HMP interface to nbd-server-start is +not changed to expose max-connections (any client needing to fine-tune +things should be using QMP). + +Suggested-by: Daniel P. Berrangé +Signed-off-by: Eric Blake +Message-ID: <20240807174943.771624-12-eblake@redhat.com> +Reviewed-by: Daniel P. Berrangé +[ericb: Expand commit message to summarize Dan's argument for why we +break corner-case back-compat behavior without a deprecation period] +Signed-off-by: Eric Blake + +(cherry picked from commit c8a76dbd90c2f48df89b75bef74917f90a59b623) +Jira: https://issues.redhat.com/browse/RHEL-52616 +Signed-off-by: Eric Blake +--- + block/monitor/block-hmp-cmds.c | 3 ++- + blockdev-nbd.c | 8 ++++++++ + include/block/nbd.h | 7 +++++++ + qapi/block-export.json | 4 ++-- + 4 files changed, 19 insertions(+), 3 deletions(-) + +diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c +index bdbb5cb141..2d7b4d80c8 100644 +--- a/block/monitor/block-hmp-cmds.c ++++ b/block/monitor/block-hmp-cmds.c +@@ -402,7 +402,8 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict) + goto exit; + } + +- nbd_server_start(addr, NULL, NULL, 0, &local_err); ++ nbd_server_start(addr, NULL, NULL, NBD_DEFAULT_MAX_CONNECTIONS, ++ &local_err); + qapi_free_SocketAddress(addr); + if (local_err != NULL) { + goto exit; +diff --git a/blockdev-nbd.c b/blockdev-nbd.c +index 267a1de903..24ba5382db 100644 +--- a/blockdev-nbd.c ++++ b/blockdev-nbd.c +@@ -170,6 +170,10 @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds, + + void nbd_server_start_options(NbdServerOptions *arg, Error **errp) + { ++ if (!arg->has_max_connections) { ++ arg->max_connections = NBD_DEFAULT_MAX_CONNECTIONS; ++ } ++ + nbd_server_start(arg->addr, arg->tls_creds, arg->tls_authz, + arg->max_connections, errp); + } +@@ -182,6 +186,10 @@ void qmp_nbd_server_start(SocketAddressLegacy *addr, + { + SocketAddress *addr_flat = socket_address_flatten(addr); + ++ if (!has_max_connections) { ++ max_connections = NBD_DEFAULT_MAX_CONNECTIONS; ++ } ++ + nbd_server_start(addr_flat, tls_creds, tls_authz, max_connections, errp); + qapi_free_SocketAddress(addr_flat); + } +diff --git a/include/block/nbd.h b/include/block/nbd.h +index 1d4d65922d..d4f8b21aec 100644 +--- a/include/block/nbd.h ++++ b/include/block/nbd.h +@@ -39,6 +39,13 @@ extern const BlockExportDriver blk_exp_nbd; + */ + #define NBD_DEFAULT_HANDSHAKE_MAX_SECS 10 + ++/* ++ * NBD_DEFAULT_MAX_CONNECTIONS: Number of client sockets to allow at ++ * once; must be large enough to allow a MULTI_CONN-aware client like ++ * nbdcopy to create its typical number of 8-16 sockets. ++ */ ++#define NBD_DEFAULT_MAX_CONNECTIONS 100 ++ + /* Handshake phase structs - this struct is passed on the wire */ + + typedef struct NBDOption { +diff --git a/qapi/block-export.json b/qapi/block-export.json +index 7874a49ba7..1d255d77e3 100644 +--- a/qapi/block-export.json ++++ b/qapi/block-export.json +@@ -28,7 +28,7 @@ + # @max-connections: The maximum number of connections to allow at the + # same time, 0 for unlimited. Setting this to 1 also stops the + # server from advertising multiple client support (since 5.2; +-# default: 0) ++# default: 100) + # + # Since: 4.2 + ## +@@ -63,7 +63,7 @@ + # @max-connections: The maximum number of connections to allow at the + # same time, 0 for unlimited. Setting this to 1 also stops the + # server from advertising multiple client support (since 5.2; +-# default: 0). ++# default: 100). + # + # Returns: error if the server is already running. + # +-- +2.39.3 + diff --git a/SOURCES/kvm-nbd-server-CVE-2024-7409-Close-stray-clients-at-serv.patch b/SOURCES/kvm-nbd-server-CVE-2024-7409-Close-stray-clients-at-serv.patch new file mode 100644 index 0000000..834eae3 --- /dev/null +++ b/SOURCES/kvm-nbd-server-CVE-2024-7409-Close-stray-clients-at-serv.patch @@ -0,0 +1,173 @@ +From 1c2fe81349a99cf0c28549426c7ad0c06d942ae3 Mon Sep 17 00:00:00 2001 +From: Eric Blake +Date: Wed, 7 Aug 2024 12:23:13 -0500 +Subject: [PATCH 5/5] nbd/server: CVE-2024-7409: Close stray clients at + server-stop +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Eric Blake +RH-MergeRequest: 386: nbd/server: fix CVE-2024-7409 (qemu crash on nbd-server-stop) [rhel-9.4.z] +RH-Jira: RHEL-52616 +RH-Acked-by: Miroslav Rezanina +RH-Commit: [4/4] 54d56c0d92d92b2b6a0f81a01853881286beae0e (ebblake/qemu-kvm) + +A malicious client can attempt to connect to an NBD server, and then +intentionally delay progress in the handshake, including if it does +not know the TLS secrets. Although the previous two patches reduce +this behavior by capping the default max-connections parameter and +killing slow clients, they did not eliminate the possibility of a +client waiting to close the socket until after the QMP nbd-server-stop +command is executed, at which point qemu would SEGV when trying to +dereference the NULL nbd_server global which is no longer present. +This amounts to a denial of service attack. Worse, if another NBD +server is started before the malicious client disconnects, I cannot +rule out additional adverse effects when the old client interferes +with the connection count of the new server (although the most likely +is a crash due to an assertion failure when checking +nbd_server->connections > 0). + +For environments without this patch, the CVE can be mitigated by +ensuring (such as via a firewall) that only trusted clients can +connect to an NBD server. Note that using frameworks like libvirt +that ensure that TLS is used and that nbd-server-stop is not executed +while any trusted clients are still connected will only help if there +is also no possibility for an untrusted client to open a connection +but then stall on the NBD handshake. + +Given the previous patches, it would be possible to guarantee that no +clients remain connected by having nbd-server-stop sleep for longer +than the default handshake deadline before finally freeing the global +nbd_server object, but that could make QMP non-responsive for a long +time. So intead, this patch fixes the problem by tracking all client +sockets opened while the server is running, and forcefully closing any +such sockets remaining without a completed handshake at the time of +nbd-server-stop, then waiting until the coroutines servicing those +sockets notice the state change. nbd-server-stop now has a second +AIO_WAIT_WHILE_UNLOCKED (the first is indirectly through the +blk_exp_close_all_type() that disconnects all clients that completed +handshakes), but forced socket shutdown is enough to progress the +coroutines and quickly tear down all clients before the server is +freed, thus finally fixing the CVE. + +This patch relies heavily on the fact that nbd/server.c guarantees +that it only calls nbd_blockdev_client_closed() from the main loop +(see the assertion in nbd_client_put() and the hoops used in +nbd_client_put_nonzero() to achieve that); if we did not have that +guarantee, we would also need a mutex protecting our accesses of the +list of connections to survive re-entrancy from independent iothreads. + +Although I did not actually try to test old builds, it looks like this +problem has existed since at least commit 862172f45c (v2.12.0, 2017) - +even back when that patch started using a QIONetListener to handle +listening on multiple sockets, nbd_server_free() was already unaware +that the nbd_blockdev_client_closed callback can be reached later by a +client thread that has not completed handshakes (and therefore the +client's socket never got added to the list closed in +nbd_export_close_all), despite that patch intentionally tearing down +the QIONetListener to prevent new clients. + +Reported-by: Alexander Ivanov +Fixes: CVE-2024-7409 +CC: qemu-stable@nongnu.org +Signed-off-by: Eric Blake +Message-ID: <20240807174943.771624-14-eblake@redhat.com> +Reviewed-by: Daniel P. Berrangé + +(cherry picked from commit 3e7ef738c8462c45043a1d39f702a0990406a3b3) +Jira: https://issues.redhat.com/browse/RHEL-52616 +Signed-off-by: Eric Blake +--- + blockdev-nbd.c | 35 ++++++++++++++++++++++++++++++++++- + 1 file changed, 34 insertions(+), 1 deletion(-) + +diff --git a/blockdev-nbd.c b/blockdev-nbd.c +index 24ba5382db..f73409ae49 100644 +--- a/blockdev-nbd.c ++++ b/blockdev-nbd.c +@@ -21,12 +21,18 @@ + #include "io/channel-socket.h" + #include "io/net-listener.h" + ++typedef struct NBDConn { ++ QIOChannelSocket *cioc; ++ QLIST_ENTRY(NBDConn) next; ++} NBDConn; ++ + typedef struct NBDServerData { + QIONetListener *listener; + QCryptoTLSCreds *tlscreds; + char *tlsauthz; + uint32_t max_connections; + uint32_t connections; ++ QLIST_HEAD(, NBDConn) conns; + } NBDServerData; + + static NBDServerData *nbd_server; +@@ -51,6 +57,14 @@ int nbd_server_max_connections(void) + + static void nbd_blockdev_client_closed(NBDClient *client, bool ignored) + { ++ NBDConn *conn = nbd_client_owner(client); ++ ++ assert(qemu_in_main_thread() && nbd_server); ++ ++ object_unref(OBJECT(conn->cioc)); ++ QLIST_REMOVE(conn, next); ++ g_free(conn); ++ + nbd_client_put(client); + assert(nbd_server->connections > 0); + nbd_server->connections--; +@@ -60,14 +74,20 @@ static void nbd_blockdev_client_closed(NBDClient *client, bool ignored) + static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc, + gpointer opaque) + { ++ NBDConn *conn = g_new0(NBDConn, 1); ++ ++ assert(qemu_in_main_thread() && nbd_server); + nbd_server->connections++; ++ object_ref(OBJECT(cioc)); ++ conn->cioc = cioc; ++ QLIST_INSERT_HEAD(&nbd_server->conns, conn, next); + nbd_update_server_watch(nbd_server); + + qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server"); + /* TODO - expose handshake timeout as QMP option */ + nbd_client_new(cioc, NBD_DEFAULT_HANDSHAKE_MAX_SECS, + nbd_server->tlscreds, nbd_server->tlsauthz, +- nbd_blockdev_client_closed, NULL); ++ nbd_blockdev_client_closed, conn); + } + + static void nbd_update_server_watch(NBDServerData *s) +@@ -81,12 +101,25 @@ static void nbd_update_server_watch(NBDServerData *s) + + static void nbd_server_free(NBDServerData *server) + { ++ NBDConn *conn, *tmp; ++ + if (!server) { + return; + } + ++ /* ++ * Forcefully close the listener socket, and any clients that have ++ * not yet disconnected on their own. ++ */ + qio_net_listener_disconnect(server->listener); + object_unref(OBJECT(server->listener)); ++ QLIST_FOREACH_SAFE(conn, &server->conns, next, tmp) { ++ qio_channel_shutdown(QIO_CHANNEL(conn->cioc), QIO_CHANNEL_SHUTDOWN_BOTH, ++ NULL); ++ } ++ ++ AIO_WAIT_WHILE_UNLOCKED(NULL, server->connections > 0); ++ + if (server->tlscreds) { + object_unref(OBJECT(server->tlscreds)); + } +-- +2.39.3 + diff --git a/SOURCES/kvm-nbd-server-CVE-2024-7409-Drop-non-negotiating-client.patch b/SOURCES/kvm-nbd-server-CVE-2024-7409-Drop-non-negotiating-client.patch new file mode 100644 index 0000000..0b2261e --- /dev/null +++ b/SOURCES/kvm-nbd-server-CVE-2024-7409-Drop-non-negotiating-client.patch @@ -0,0 +1,134 @@ +From 71c525e45f3f2a421ad651bc50eff94be925b36c Mon Sep 17 00:00:00 2001 +From: Eric Blake +Date: Thu, 8 Aug 2024 16:05:08 -0500 +Subject: [PATCH 4/5] nbd/server: CVE-2024-7409: Drop non-negotiating clients +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Eric Blake +RH-MergeRequest: 386: nbd/server: fix CVE-2024-7409 (qemu crash on nbd-server-stop) [rhel-9.4.z] +RH-Jira: RHEL-52616 +RH-Acked-by: Miroslav Rezanina +RH-Commit: [3/4] 4394e60a8733cd57ee6c8cb140171c7f61cc13a3 (ebblake/qemu-kvm) + +A client that opens a socket but does not negotiate is merely hogging +qemu's resources (an open fd and a small amount of memory); and a +malicious client that can access the port where NBD is listening can +attempt a denial of service attack by intentionally opening and +abandoning lots of unfinished connections. The previous patch put a +default bound on the number of such ongoing connections, but once that +limit is hit, no more clients can connect (including legitimate ones). +The solution is to insist that clients complete handshake within a +reasonable time limit, defaulting to 10 seconds. A client that has +not successfully completed NBD_OPT_GO by then (including the case of +where the client didn't know TLS credentials to even reach the point +of NBD_OPT_GO) is wasting our time and does not deserve to stay +connected. Later patches will allow fine-tuning the limit away from +the default value (including disabling it for doing integration +testing of the handshake process itself). + +Note that this patch in isolation actually makes it more likely to see +qemu SEGV after nbd-server-stop, as any client socket still connected +when the server shuts down will now be closed after 10 seconds rather +than at the client's whims. That will be addressed in the next patch. + +For a demo of this patch in action: +$ qemu-nbd -f raw -r -t -e 10 file & +$ nbdsh --opt-mode -c ' +H = list() +for i in range(20): + print(i) + H.insert(i, nbd.NBD()) + H[i].set_opt_mode(True) + H[i].connect_uri("nbd://localhost") +' +$ kill $! + +where later connections get to start progressing once earlier ones are +forcefully dropped for taking too long, rather than hanging. + +Suggested-by: Daniel P. Berrangé +Signed-off-by: Eric Blake +Message-ID: <20240807174943.771624-13-eblake@redhat.com> +Reviewed-by: Daniel P. Berrangé +[eblake: rebase to changes earlier in series, reduce scope of timer] +Signed-off-by: Eric Blake + +(cherry picked from commit b9b72cb3ce15b693148bd09cef7e50110566d8a0) +Jira: https://issues.redhat.com/browse/RHEL-52616 +Signed-off-by: Eric Blake +--- + nbd/server.c | 28 +++++++++++++++++++++++++++- + nbd/trace-events | 1 + + 2 files changed, 28 insertions(+), 1 deletion(-) + +diff --git a/nbd/server.c b/nbd/server.c +index e50012499f..39285cc971 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -3186,22 +3186,48 @@ static void nbd_client_receive_next_request(NBDClient *client) + } + } + ++static void nbd_handshake_timer_cb(void *opaque) ++{ ++ QIOChannel *ioc = opaque; ++ ++ trace_nbd_handshake_timer_cb(); ++ qio_channel_shutdown(ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); ++} ++ + static coroutine_fn void nbd_co_client_start(void *opaque) + { + NBDClient *client = opaque; + Error *local_err = NULL; ++ QEMUTimer *handshake_timer = NULL; + + qemu_co_mutex_init(&client->send_lock); + +- /* TODO - utilize client->handshake_max_secs */ ++ /* ++ * Create a timer to bound the time spent in negotiation. If the ++ * timer expires, it is likely nbd_negotiate will fail because the ++ * socket was shutdown. ++ */ ++ if (client->handshake_max_secs > 0) { ++ handshake_timer = aio_timer_new(qemu_get_aio_context(), ++ QEMU_CLOCK_REALTIME, ++ SCALE_NS, ++ nbd_handshake_timer_cb, ++ client->sioc); ++ timer_mod(handshake_timer, ++ qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ++ client->handshake_max_secs * NANOSECONDS_PER_SECOND); ++ } ++ + if (nbd_negotiate(client, &local_err)) { + if (local_err) { + error_report_err(local_err); + } ++ timer_free(handshake_timer); + client_close(client, false); + return; + } + ++ timer_free(handshake_timer); + WITH_QEMU_LOCK_GUARD(&client->lock) { + nbd_client_receive_next_request(client); + } +diff --git a/nbd/trace-events b/nbd/trace-events +index 00ae3216a1..cbd0a4ab7e 100644 +--- a/nbd/trace-events ++++ b/nbd/trace-events +@@ -76,6 +76,7 @@ nbd_co_receive_request_payload_received(uint64_t cookie, uint64_t len) "Payload + nbd_co_receive_ext_payload_compliance(uint64_t from, uint64_t len) "client sent non-compliant write without payload flag: from=0x%" PRIx64 ", len=0x%" PRIx64 + nbd_co_receive_align_compliance(const char *op, uint64_t from, uint64_t len, uint32_t align) "client sent non-compliant unaligned %s request: from=0x%" PRIx64 ", len=0x%" PRIx64 ", align=0x%" PRIx32 + nbd_trip(void) "Reading request" ++nbd_handshake_timer_cb(void) "client took too long to negotiate" + + # client-connection.c + nbd_connect_thread_sleep(uint64_t timeout) "timeout %" PRIu64 +-- +2.39.3 + diff --git a/SOURCES/kvm-nbd-server-Plumb-in-new-args-to-nbd_client_add.patch b/SOURCES/kvm-nbd-server-Plumb-in-new-args-to-nbd_client_add.patch new file mode 100644 index 0000000..85a9986 --- /dev/null +++ b/SOURCES/kvm-nbd-server-Plumb-in-new-args-to-nbd_client_add.patch @@ -0,0 +1,175 @@ +From c8c7dc7a445892a820223be2e7617a790e0400f5 Mon Sep 17 00:00:00 2001 +From: Eric Blake +Date: Wed, 7 Aug 2024 08:50:01 -0500 +Subject: [PATCH 2/5] nbd/server: Plumb in new args to nbd_client_add() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Eric Blake +RH-MergeRequest: 386: nbd/server: fix CVE-2024-7409 (qemu crash on nbd-server-stop) [rhel-9.4.z] +RH-Jira: RHEL-52616 +RH-Acked-by: Miroslav Rezanina +RH-Commit: [1/4] 658d80c80a1163fcad456a34ec20d7828273a36c (ebblake/qemu-kvm) + +Upcoming patches to fix a CVE need to track an opaque pointer passed +in by the owner of a client object, as well as request for a time +limit on how fast negotiation must complete. Prepare for that by +changing the signature of nbd_client_new() and adding an accessor to +get at the opaque pointer, although for now the two servers +(qemu-nbd.c and blockdev-nbd.c) do not change behavior even though +they pass in a new default timeout value. + +Suggested-by: Vladimir Sementsov-Ogievskiy +Signed-off-by: Eric Blake +Message-ID: <20240807174943.771624-11-eblake@redhat.com> +Reviewed-by: Daniel P. Berrangé +[eblake: s/LIMIT/MAX_SECS/ as suggested by Dan] +Signed-off-by: Eric Blake + +(cherry picked from commit fb1c2aaa981e0a2fa6362c9985f1296b74f055ac) +Jira: https://issues.redhat.com/browse/RHEL-52616 +Signed-off-by: Eric Blake +--- + blockdev-nbd.c | 6 ++++-- + include/block/nbd.h | 11 ++++++++++- + nbd/server.c | 20 +++++++++++++++++--- + qemu-nbd.c | 4 +++- + 4 files changed, 34 insertions(+), 7 deletions(-) + +diff --git a/blockdev-nbd.c b/blockdev-nbd.c +index 213012435f..267a1de903 100644 +--- a/blockdev-nbd.c ++++ b/blockdev-nbd.c +@@ -64,8 +64,10 @@ static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc, + nbd_update_server_watch(nbd_server); + + qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server"); +- nbd_client_new(cioc, nbd_server->tlscreds, nbd_server->tlsauthz, +- nbd_blockdev_client_closed); ++ /* TODO - expose handshake timeout as QMP option */ ++ nbd_client_new(cioc, NBD_DEFAULT_HANDSHAKE_MAX_SECS, ++ nbd_server->tlscreds, nbd_server->tlsauthz, ++ nbd_blockdev_client_closed, NULL); + } + + static void nbd_update_server_watch(NBDServerData *s) +diff --git a/include/block/nbd.h b/include/block/nbd.h +index 4e7bd6342f..1d4d65922d 100644 +--- a/include/block/nbd.h ++++ b/include/block/nbd.h +@@ -33,6 +33,12 @@ typedef struct NBDMetaContexts NBDMetaContexts; + + extern const BlockExportDriver blk_exp_nbd; + ++/* ++ * NBD_DEFAULT_HANDSHAKE_MAX_SECS: Number of seconds in which client must ++ * succeed at NBD_OPT_GO before being forcefully dropped as too slow. ++ */ ++#define NBD_DEFAULT_HANDSHAKE_MAX_SECS 10 ++ + /* Handshake phase structs - this struct is passed on the wire */ + + typedef struct NBDOption { +@@ -403,9 +409,12 @@ AioContext *nbd_export_aio_context(NBDExport *exp); + NBDExport *nbd_export_find(const char *name); + + void nbd_client_new(QIOChannelSocket *sioc, ++ uint32_t handshake_max_secs, + QCryptoTLSCreds *tlscreds, + const char *tlsauthz, +- void (*close_fn)(NBDClient *, bool)); ++ void (*close_fn)(NBDClient *, bool), ++ void *owner); ++void *nbd_client_owner(NBDClient *client); + void nbd_client_get(NBDClient *client); + void nbd_client_put(NBDClient *client); + +diff --git a/nbd/server.c b/nbd/server.c +index 892797bb11..e50012499f 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -124,12 +124,14 @@ struct NBDMetaContexts { + struct NBDClient { + int refcount; /* atomic */ + void (*close_fn)(NBDClient *client, bool negotiated); ++ void *owner; + + QemuMutex lock; + + NBDExport *exp; + QCryptoTLSCreds *tlscreds; + char *tlsauthz; ++ uint32_t handshake_max_secs; + QIOChannelSocket *sioc; /* The underlying data channel */ + QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */ + +@@ -3191,6 +3193,7 @@ static coroutine_fn void nbd_co_client_start(void *opaque) + + qemu_co_mutex_init(&client->send_lock); + ++ /* TODO - utilize client->handshake_max_secs */ + if (nbd_negotiate(client, &local_err)) { + if (local_err) { + error_report_err(local_err); +@@ -3205,14 +3208,17 @@ static coroutine_fn void nbd_co_client_start(void *opaque) + } + + /* +- * Create a new client listener using the given channel @sioc. ++ * Create a new client listener using the given channel @sioc and @owner. + * Begin servicing it in a coroutine. When the connection closes, call +- * @close_fn with an indication of whether the client completed negotiation. ++ * @close_fn with an indication of whether the client completed negotiation ++ * within @handshake_max_secs seconds (0 for unbounded). + */ + void nbd_client_new(QIOChannelSocket *sioc, ++ uint32_t handshake_max_secs, + QCryptoTLSCreds *tlscreds, + const char *tlsauthz, +- void (*close_fn)(NBDClient *, bool)) ++ void (*close_fn)(NBDClient *, bool), ++ void *owner) + { + NBDClient *client; + Coroutine *co; +@@ -3225,13 +3231,21 @@ void nbd_client_new(QIOChannelSocket *sioc, + object_ref(OBJECT(client->tlscreds)); + } + client->tlsauthz = g_strdup(tlsauthz); ++ client->handshake_max_secs = handshake_max_secs; + client->sioc = sioc; + qio_channel_set_delay(QIO_CHANNEL(sioc), false); + object_ref(OBJECT(client->sioc)); + client->ioc = QIO_CHANNEL(sioc); + object_ref(OBJECT(client->ioc)); + client->close_fn = close_fn; ++ client->owner = owner; + + co = qemu_coroutine_create(nbd_co_client_start, client); + qemu_coroutine_enter(co); + } ++ ++void * ++nbd_client_owner(NBDClient *client) ++{ ++ return client->owner; ++} +diff --git a/qemu-nbd.c b/qemu-nbd.c +index bac0b5e3ec..01b4a63c31 100644 +--- a/qemu-nbd.c ++++ b/qemu-nbd.c +@@ -389,7 +389,9 @@ static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc, + + nb_fds++; + nbd_update_server_watch(); +- nbd_client_new(cioc, tlscreds, tlsauthz, nbd_client_closed); ++ /* TODO - expose handshake timeout as command line option */ ++ nbd_client_new(cioc, NBD_DEFAULT_HANDSHAKE_MAX_SECS, ++ tlscreds, tlsauthz, nbd_client_closed, NULL); + } + + static void nbd_update_server_watch(void) +-- +2.39.3 + diff --git a/SOURCES/kvm-rhel-9.4.0-machine-type-compat-for-virtio-gpu-migrat.patch b/SOURCES/kvm-rhel-9.4.0-machine-type-compat-for-virtio-gpu-migrat.patch new file mode 100644 index 0000000..667e80b --- /dev/null +++ b/SOURCES/kvm-rhel-9.4.0-machine-type-compat-for-virtio-gpu-migrat.patch @@ -0,0 +1,38 @@ +From aa0ad36a56cbc0528306e47112081b0db124e512 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= +Date: Wed, 5 Jun 2024 10:28:20 +0400 +Subject: [PATCH 3/4] rhel 9.4.0 machine type compat for virtio-gpu migration +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Marc-André Lureau +RH-MergeRequest: 377: virtio-gpu: fix scanout migration post-load +RH-Jira: RHEL-36181 +RH-Acked-by: Peter Xu +RH-Acked-by: Miroslav Rezanina +RH-Commit: [3/3] 270e932e04eb2d730550f3a56b53cbb17f5e66f8 + +Jira: https://issues.redhat.com/browse/RHEL-36181 + +Signed-off-by: Marc-André Lureau +--- + hw/core/machine.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/hw/core/machine.c b/hw/core/machine.c +index a0843ab93e..c8c460c916 100644 +--- a/hw/core/machine.c ++++ b/hw/core/machine.c +@@ -81,6 +81,8 @@ GlobalProperty hw_compat_rhel_9_4[] = { + { "igb", "x-pcie-flr-init", "off" }, + /* hw_compat_rhel_9_4 jira RHEL-24045 */ + { "virtio-mem", "dynamic-memslots", "off" }, ++ /* hw_compat_rhel_9_4 from hw_compat_8_1 */ ++ { "virtio-gpu-device", "x-scanout-vmstate-version", "1" }, + }; + const size_t hw_compat_rhel_9_4_len = G_N_ELEMENTS(hw_compat_rhel_9_4); + +-- +2.39.3 + diff --git a/SOURCES/kvm-virtio-gpu-fix-scanout-migration-post-load.patch b/SOURCES/kvm-virtio-gpu-fix-scanout-migration-post-load.patch new file mode 100644 index 0000000..b1581bf --- /dev/null +++ b/SOURCES/kvm-virtio-gpu-fix-scanout-migration-post-load.patch @@ -0,0 +1,196 @@ +From 523423436e83b01a83c60bc44bd08471992aaff4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= +Date: Mon, 15 Jan 2024 16:34:33 +0400 +Subject: [PATCH 1/4] virtio-gpu: fix scanout migration post-load +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Marc-André Lureau +RH-MergeRequest: 377: virtio-gpu: fix scanout migration post-load +RH-Jira: RHEL-36181 +RH-Acked-by: Peter Xu +RH-Acked-by: Miroslav Rezanina +RH-Commit: [1/3] 05e9dbf7f602d62ec2f336a855daf57e6ee8f3f3 + +The current post-loading code for scanout has a FIXME: it doesn't take +the resource region/rect into account. But there is more, when adding +blob migration support in commit f66767f75c9, I didn't realize that blob +resources could be used for scanouts. This situationn leads to a crash +during post-load, as they don't have an associated res->image. + +virtio_gpu_do_set_scanout() handle all cases, but requires the +associated virtio_gpu_framebuffer, which is currently not saved during +migration. + +Add a v2 of "virtio-gpu-one-scanout" with the framebuffer fields, so we +can restore blob scanouts, as well as fixing the existing FIXME. + +Signed-off-by: Marc-André Lureau +Reviewed-by: Sebastian Ott +(cherry picked from commit dfcf74fa68c88233209aafc5f82728d0b9a1af5c) +--- + hw/display/virtio-gpu.c | 55 +++++++++++++++++++++++++++------- + include/hw/virtio/virtio-gpu.h | 1 + + 2 files changed, 46 insertions(+), 10 deletions(-) + +diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c +index 1702190ead..bc485d60ea 100644 +--- a/hw/display/virtio-gpu.c ++++ b/hw/display/virtio-gpu.c +@@ -598,6 +598,7 @@ static void virtio_unref_resource(pixman_image_t *image, void *data) + static void virtio_gpu_update_scanout(VirtIOGPU *g, + uint32_t scanout_id, + struct virtio_gpu_simple_resource *res, ++ struct virtio_gpu_framebuffer *fb, + struct virtio_gpu_rect *r) + { + struct virtio_gpu_simple_resource *ores; +@@ -615,9 +616,10 @@ static void virtio_gpu_update_scanout(VirtIOGPU *g, + scanout->y = r->y; + scanout->width = r->width; + scanout->height = r->height; ++ scanout->fb = *fb; + } + +-static void virtio_gpu_do_set_scanout(VirtIOGPU *g, ++static bool virtio_gpu_do_set_scanout(VirtIOGPU *g, + uint32_t scanout_id, + struct virtio_gpu_framebuffer *fb, + struct virtio_gpu_simple_resource *res, +@@ -643,7 +645,7 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g, + r->x, r->y, r->width, r->height, + fb->width, fb->height); + *error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; +- return; ++ return false; + } + + g->parent_obj.enable = 1; +@@ -651,11 +653,12 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g, + if (res->blob) { + if (console_has_gl(scanout->con)) { + if (!virtio_gpu_update_dmabuf(g, scanout_id, res, fb, r)) { +- virtio_gpu_update_scanout(g, scanout_id, res, r); ++ virtio_gpu_update_scanout(g, scanout_id, res, fb, r); + } else { + *error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY; ++ return false; + } +- return; ++ return true; + } + + data = res->blob; +@@ -684,7 +687,7 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g, + scanout->ds = qemu_create_displaysurface_pixman(rect); + if (!scanout->ds) { + *error = VIRTIO_GPU_RESP_ERR_UNSPEC; +- return; ++ return false; + } + #ifdef WIN32 + qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, fb->offset); +@@ -695,7 +698,8 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g, + scanout->ds); + } + +- virtio_gpu_update_scanout(g, scanout_id, res, r); ++ virtio_gpu_update_scanout(g, scanout_id, res, fb, r); ++ return true; + } + + static void virtio_gpu_set_scanout(VirtIOGPU *g, +@@ -1166,8 +1170,9 @@ static void virtio_gpu_cursor_bh(void *opaque) + + static const VMStateDescription vmstate_virtio_gpu_scanout = { + .name = "virtio-gpu-one-scanout", +- .version_id = 1, +- .fields = (VMStateField[]) { ++ .version_id = 2, ++ .minimum_version_id = 1, ++ .fields = (const VMStateField[]) { + VMSTATE_UINT32(resource_id, struct virtio_gpu_scanout), + VMSTATE_UINT32(width, struct virtio_gpu_scanout), + VMSTATE_UINT32(height, struct virtio_gpu_scanout), +@@ -1178,6 +1183,12 @@ static const VMStateDescription vmstate_virtio_gpu_scanout = { + VMSTATE_UINT32(cursor.hot_y, struct virtio_gpu_scanout), + VMSTATE_UINT32(cursor.pos.x, struct virtio_gpu_scanout), + VMSTATE_UINT32(cursor.pos.y, struct virtio_gpu_scanout), ++ VMSTATE_UINT32_V(fb.format, struct virtio_gpu_scanout, 2), ++ VMSTATE_UINT32_V(fb.bytes_pp, struct virtio_gpu_scanout, 2), ++ VMSTATE_UINT32_V(fb.width, struct virtio_gpu_scanout, 2), ++ VMSTATE_UINT32_V(fb.height, struct virtio_gpu_scanout, 2), ++ VMSTATE_UINT32_V(fb.stride, struct virtio_gpu_scanout, 2), ++ VMSTATE_UINT32_V(fb.offset, struct virtio_gpu_scanout, 2), + VMSTATE_END_OF_LIST() + }, + }; +@@ -1349,6 +1360,7 @@ static int virtio_gpu_blob_save(QEMUFile *f, void *opaque, size_t size, + if (!res->blob_size) { + continue; + } ++ assert(!res->image); + qemu_put_be32(f, res->resource_id); + qemu_put_be32(f, res->blob_size); + qemu_put_be32(f, res->iov_cnt); +@@ -1411,11 +1423,11 @@ static int virtio_gpu_post_load(void *opaque, int version_id) + int i; + + for (i = 0; i < g->parent_obj.conf.max_outputs; i++) { +- /* FIXME: should take scanout.r.{x,y} into account */ + scanout = &g->parent_obj.scanout[i]; + if (!scanout->resource_id) { + continue; + } ++ + res = virtio_gpu_find_resource(g, scanout->resource_id); + if (!res) { + return -EINVAL; +@@ -1428,7 +1440,30 @@ static int virtio_gpu_post_load(void *opaque, int version_id) + qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, 0); + #endif + +- dpy_gfx_replace_surface(scanout->con, scanout->ds); ++ if (scanout->fb.format != 0) { ++ uint32_t error = 0; ++ struct virtio_gpu_rect r = { ++ .x = scanout->x, ++ .y = scanout->y, ++ .width = scanout->width, ++ .height = scanout->height ++ }; ++ ++ if (!virtio_gpu_do_set_scanout(g, i, &scanout->fb, res, &r, &error)) { ++ return -EINVAL; ++ } ++ } else { ++ /* legacy v1 migration support */ ++ if (!res->image) { ++ return -EINVAL; ++ } ++ scanout->ds = qemu_create_displaysurface_pixman(res->image); ++#ifdef WIN32 ++ qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, 0); ++#endif ++ dpy_gfx_replace_surface(scanout->con, scanout->ds); ++ } ++ + dpy_gfx_update_full(scanout->con); + if (scanout->cursor.resource_id) { + update_cursor(g, &scanout->cursor); +diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h +index 584ba2ed73..9a13afb34e 100644 +--- a/include/hw/virtio/virtio-gpu.h ++++ b/include/hw/virtio/virtio-gpu.h +@@ -81,6 +81,7 @@ struct virtio_gpu_scanout { + uint32_t resource_id; + struct virtio_gpu_update_cursor cursor; + QEMUCursor *current_cursor; ++ struct virtio_gpu_framebuffer fb; + }; + + struct virtio_gpu_requested_state { +-- +2.39.3 + diff --git a/SOURCES/kvm-virtio-gpu-fix-v2-migration.patch b/SOURCES/kvm-virtio-gpu-fix-v2-migration.patch new file mode 100644 index 0000000..028b23b --- /dev/null +++ b/SOURCES/kvm-virtio-gpu-fix-v2-migration.patch @@ -0,0 +1,122 @@ +From 7741a7d1c1d719ff681f73a023f27d5951b98882 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= +Date: Thu, 16 May 2024 12:40:22 +0400 +Subject: [PATCH 2/4] virtio-gpu: fix v2 migration +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Marc-André Lureau +RH-MergeRequest: 377: virtio-gpu: fix scanout migration post-load +RH-Jira: RHEL-36181 +RH-Acked-by: Peter Xu +RH-Acked-by: Miroslav Rezanina +RH-Commit: [2/3] 482c37c10ac7e5e8a6c36ac2eb806ac02beeefdc + +Commit dfcf74fa ("virtio-gpu: fix scanout migration post-load") broke +forward/backward version migration. Versioning of nested VMSD structures +is not straightforward, as the wire format doesn't have nested +structures versions. Introduce x-scanout-vmstate-version and a field +test to save/load appropriately according to the machine version. + +Fixes: dfcf74fa ("virtio-gpu: fix scanout migration post-load") +Signed-off-by: Marc-André Lureau +Signed-off-by: Peter Xu +Reviewed-by: Fiona Ebner +Tested-by: Fiona Ebner +[fixed long lines] +Signed-off-by: Fabiano Rosas + +[ Move the hw-compat from 8.2 to 8.1 ] +Signed-off-by: Marc-André Lureau +(cherry picked from commit 40a23ef643664b5c1021a9789f9d680b6294fb50) +--- + hw/core/machine.c | 1 + + hw/display/virtio-gpu.c | 30 ++++++++++++++++++++++-------- + include/hw/virtio/virtio-gpu.h | 1 + + 3 files changed, 24 insertions(+), 8 deletions(-) + +diff --git a/hw/core/machine.c b/hw/core/machine.c +index 309f6ba685..a0843ab93e 100644 +--- a/hw/core/machine.c ++++ b/hw/core/machine.c +@@ -37,6 +37,7 @@ GlobalProperty hw_compat_8_1[] = { + { "ramfb", "x-migrate", "off" }, + { "vfio-pci-nohotplug", "x-ramfb-migrate", "off" }, + { "igb", "x-pcie-flr-init", "off" }, ++ { "virtio-gpu-device", "x-scanout-vmstate-version", "1" }, + }; + const size_t hw_compat_8_1_len = G_N_ELEMENTS(hw_compat_8_1); + +diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c +index bc485d60ea..9d35a016ae 100644 +--- a/hw/display/virtio-gpu.c ++++ b/hw/display/virtio-gpu.c +@@ -1168,10 +1168,17 @@ static void virtio_gpu_cursor_bh(void *opaque) + virtio_gpu_handle_cursor(&g->parent_obj.parent_obj, g->cursor_vq); + } + ++static bool scanout_vmstate_after_v2(void *opaque, int version) ++{ ++ struct VirtIOGPUBase *base = container_of(opaque, VirtIOGPUBase, scanout); ++ struct VirtIOGPU *gpu = container_of(base, VirtIOGPU, parent_obj); ++ ++ return gpu->scanout_vmstate_version >= 2; ++} ++ + static const VMStateDescription vmstate_virtio_gpu_scanout = { + .name = "virtio-gpu-one-scanout", +- .version_id = 2, +- .minimum_version_id = 1, ++ .version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(resource_id, struct virtio_gpu_scanout), + VMSTATE_UINT32(width, struct virtio_gpu_scanout), +@@ -1183,12 +1190,18 @@ static const VMStateDescription vmstate_virtio_gpu_scanout = { + VMSTATE_UINT32(cursor.hot_y, struct virtio_gpu_scanout), + VMSTATE_UINT32(cursor.pos.x, struct virtio_gpu_scanout), + VMSTATE_UINT32(cursor.pos.y, struct virtio_gpu_scanout), +- VMSTATE_UINT32_V(fb.format, struct virtio_gpu_scanout, 2), +- VMSTATE_UINT32_V(fb.bytes_pp, struct virtio_gpu_scanout, 2), +- VMSTATE_UINT32_V(fb.width, struct virtio_gpu_scanout, 2), +- VMSTATE_UINT32_V(fb.height, struct virtio_gpu_scanout, 2), +- VMSTATE_UINT32_V(fb.stride, struct virtio_gpu_scanout, 2), +- VMSTATE_UINT32_V(fb.offset, struct virtio_gpu_scanout, 2), ++ VMSTATE_UINT32_TEST(fb.format, struct virtio_gpu_scanout, ++ scanout_vmstate_after_v2), ++ VMSTATE_UINT32_TEST(fb.bytes_pp, struct virtio_gpu_scanout, ++ scanout_vmstate_after_v2), ++ VMSTATE_UINT32_TEST(fb.width, struct virtio_gpu_scanout, ++ scanout_vmstate_after_v2), ++ VMSTATE_UINT32_TEST(fb.height, struct virtio_gpu_scanout, ++ scanout_vmstate_after_v2), ++ VMSTATE_UINT32_TEST(fb.stride, struct virtio_gpu_scanout, ++ scanout_vmstate_after_v2), ++ VMSTATE_UINT32_TEST(fb.offset, struct virtio_gpu_scanout, ++ scanout_vmstate_after_v2), + VMSTATE_END_OF_LIST() + }, + }; +@@ -1668,6 +1681,7 @@ static Property virtio_gpu_properties[] = { + DEFINE_PROP_BIT("blob", VirtIOGPU, parent_obj.conf.flags, + VIRTIO_GPU_FLAG_BLOB_ENABLED, false), + DEFINE_PROP_SIZE("hostmem", VirtIOGPU, parent_obj.conf.hostmem, 0), ++ DEFINE_PROP_UINT8("x-scanout-vmstate-version", VirtIOGPU, scanout_vmstate_version, 2), + DEFINE_PROP_END_OF_LIST(), + }; + +diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h +index 9a13afb34e..18500432c6 100644 +--- a/include/hw/virtio/virtio-gpu.h ++++ b/include/hw/virtio/virtio-gpu.h +@@ -177,6 +177,7 @@ typedef struct VGPUDMABuf { + struct VirtIOGPU { + VirtIOGPUBase parent_obj; + ++ uint8_t scanout_vmstate_version; + uint64_t conf_max_hostmem; + + VirtQueue *ctrl_vq; +-- +2.39.3 + diff --git a/SPECS/qemu-kvm.spec b/SPECS/qemu-kvm.spec index 80f9432..eeff4c6 100644 --- a/SPECS/qemu-kvm.spec +++ b/SPECS/qemu-kvm.spec @@ -149,7 +149,7 @@ Obsoletes: %{name}-block-ssh <= %{epoch}:%{version} \ Summary: QEMU is a machine emulator and virtualizer Name: qemu-kvm Version: 8.2.0 -Release: 11%{?rcrel}%{?dist}%{?cc_suffix}.4 +Release: 11%{?rcrel}%{?dist}%{?cc_suffix}.6 # Epoch because we pushed a qemu-1.0 package. AIUI this can't ever be dropped # Epoch 15 used for RHEL 8 # Epoch 17 used for RHEL 9 (due to release versioning offset in RHEL 8.5) @@ -630,6 +630,24 @@ Patch185: kvm-iotests-244-Don-t-store-data-file-with-protocol-in-i.patch Patch186: kvm-iotests-270-Don-t-store-data-file-with-json-prefix-i.patch # For RHEL-35610 Patch187: kvm-block-Parse-filenames-only-when-explicitly-requested.patch +# For RHEL-36181 - [RHEL9.5.0][stable_guest_abi]Failed to migrate VM with (qemu) qemu-kvm: Missing section footer for 0000:00:01.0/virtio-gpu qemu-kvm: load of migration failed: Invalid argument [rhel-9.4.0.z] +Patch188: kvm-virtio-gpu-fix-scanout-migration-post-load.patch +# For RHEL-36181 - [RHEL9.5.0][stable_guest_abi]Failed to migrate VM with (qemu) qemu-kvm: Missing section footer for 0000:00:01.0/virtio-gpu qemu-kvm: load of migration failed: Invalid argument [rhel-9.4.0.z] +Patch189: kvm-virtio-gpu-fix-v2-migration.patch +# For RHEL-36181 - [RHEL9.5.0][stable_guest_abi]Failed to migrate VM with (qemu) qemu-kvm: Missing section footer for 0000:00:01.0/virtio-gpu qemu-kvm: load of migration failed: Invalid argument [rhel-9.4.0.z] +Patch190: kvm-rhel-9.4.0-machine-type-compat-for-virtio-gpu-migrat.patch +# For RHEL-43261 - qemu-kvm: linux-aio: add support for IO_CMD_FDSYNC command [rhel-9.4.z] +Patch191: kvm-linux-aio-add-IO_CMD_FDSYNC-command-support.patch +# For RHEL-53565 - [RHEL9.4_to_RHEL9.5]When the VM is with only 9.4.0 (q35) machine type, still hit error of virtio-gpu issue [rhel-9.4.z] +Patch192: kvm-Fix-scanout-version-with-pc-q35-rhel9.4.0.patch +# For RHEL-52616 - CVE-2024-7409 qemu-kvm: Denial of Service via Improper Synchronization in QEMU NBD Server During Socket Closure [rhel-9.4.z] +Patch193: kvm-nbd-server-Plumb-in-new-args-to-nbd_client_add.patch +# For RHEL-52616 - CVE-2024-7409 qemu-kvm: Denial of Service via Improper Synchronization in QEMU NBD Server During Socket Closure [rhel-9.4.z] +Patch194: kvm-nbd-server-CVE-2024-7409-Cap-default-max-connections.patch +# For RHEL-52616 - CVE-2024-7409 qemu-kvm: Denial of Service via Improper Synchronization in QEMU NBD Server During Socket Closure [rhel-9.4.z] +Patch195: kvm-nbd-server-CVE-2024-7409-Drop-non-negotiating-client.patch +# For RHEL-52616 - CVE-2024-7409 qemu-kvm: Denial of Service via Improper Synchronization in QEMU NBD Server During Socket Closure [rhel-9.4.z] +Patch196: kvm-nbd-server-CVE-2024-7409-Close-stray-clients-at-serv.patch %if %{have_clang} BuildRequires: clang @@ -1691,6 +1709,27 @@ useradd -r -u 107 -g qemu -G kvm -d / -s /sbin/nologin \ %endif %changelog +* Thu Aug 15 2024 Miroslav Rezanina - 8.2.0-11.el9_4.6 +- kvm-Fix-scanout-version-with-pc-q35-rhel9.4.0.patch [RHEL-53565] +- kvm-nbd-server-Plumb-in-new-args-to-nbd_client_add.patch [RHEL-52616] +- kvm-nbd-server-CVE-2024-7409-Cap-default-max-connections.patch [RHEL-52616] +- kvm-nbd-server-CVE-2024-7409-Drop-non-negotiating-client.patch [RHEL-52616] +- kvm-nbd-server-CVE-2024-7409-Close-stray-clients-at-serv.patch [RHEL-52616] +- Resolves: RHEL-53565 + ([RHEL9.4_to_RHEL9.5]When the VM is with only 9.4.0 (q35) machine type, still hit error of virtio-gpu issue [rhel-9.4.z]) +- Resolves: RHEL-52616 + (CVE-2024-7409 qemu-kvm: Denial of Service via Improper Synchronization in QEMU NBD Server During Socket Closure [rhel-9.4.z]) + +* Mon Jul 15 2024 Miroslav Rezanina - 8.2.0-11.el9_4.5 +- kvm-virtio-gpu-fix-scanout-migration-post-load.patch [RHEL-36181] +- kvm-virtio-gpu-fix-v2-migration.patch [RHEL-36181] +- kvm-rhel-9.4.0-machine-type-compat-for-virtio-gpu-migrat.patch [RHEL-36181] +- kvm-linux-aio-add-IO_CMD_FDSYNC-command-support.patch [RHEL-43261] +- Resolves: RHEL-36181 + ([RHEL9.5.0][stable_guest_abi]Failed to migrate VM with (qemu) qemu-kvm: Missing section footer for 0000:00:01.0/virtio-gpu qemu-kvm: load of migration failed: Invalid argument [rhel-9.4.0.z]) +- Resolves: RHEL-43261 + (qemu-kvm: linux-aio: add support for IO_CMD_FDSYNC command [rhel-9.4.z]) + * Wed Jun 19 2024 Miroslav Rezanina - 8.2.0-11.el9_4.4 - Fixing CVE-2024-4467 - Resolves: RHEL-35610