From 30e4a5e1f7cdf0ddbd4008057962244d4e5900ed Mon Sep 17 00:00:00 2001 From: eabdullin Date: Tue, 10 Feb 2026 16:50:11 +0000 Subject: [PATCH] Import from CS git --- ...ter-free-in-websocket-handshake-code.patch | 189 ++++++++++++++++++ ...ock-resource-release-to-close-method.patch | 84 ++++++++ ...-skip-callback-for-sgio-error-status.patch | 79 ++++++++ ...rning-comments-that-host_status-erro.patch | 79 ++++++++ ...-report-RESERVATION_CONFLICT-to-gues.patch | 105 ++++++++++ ...error-policy-for-host_status-errors-.patch | 155 ++++++++++++++ ...sitive-return-value-for-status-in-dm.patch | 125 ++++++++++++ SPECS/qemu-kvm.spec | 31 ++- 8 files changed, 846 insertions(+), 1 deletion(-) create mode 100644 SOURCES/kvm-io-fix-use-after-free-in-websocket-handshake-code.patch create mode 100644 SOURCES/kvm-io-move-websock-resource-release-to-close-method.patch create mode 100644 SOURCES/kvm-scsi-block-Don-t-skip-callback-for-sgio-error-status.patch create mode 100644 SOURCES/kvm-scsi-disk-Add-warning-comments-that-host_status-erro.patch create mode 100644 SOURCES/kvm-scsi-disk-Always-report-RESERVATION_CONFLICT-to-gues.patch create mode 100644 SOURCES/kvm-scsi-disk-Apply-error-policy-for-host_status-errors-.patch create mode 100644 SOURCES/kvm-scsi-disk-Use-positive-return-value-for-status-in-dm.patch diff --git a/SOURCES/kvm-io-fix-use-after-free-in-websocket-handshake-code.patch b/SOURCES/kvm-io-fix-use-after-free-in-websocket-handshake-code.patch new file mode 100644 index 0000000..775f112 --- /dev/null +++ b/SOURCES/kvm-io-fix-use-after-free-in-websocket-handshake-code.patch @@ -0,0 +1,189 @@ +From b57e930ad6e2e2aea35c046a2a5ea6b04c472c5b Mon Sep 17 00:00:00 2001 +From: Jon Maloy +Date: Tue, 4 Nov 2025 17:28:47 -0500 +Subject: [PATCH 2/2] io: fix use after free in websocket handshake code +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Jon Maloy +RH-MergeRequest: 499: io: fix use after free in websocket handshake code +RH-Jira: RHEL-120119 +RH-Acked-by: Daniel P. Berrangé +RH-Acked-by: Eric Blake +RH-Commit: [2/2] d9a0db83d83e8275decdba615338a9dee3a8d33c (redhat/rhel/src/qemu-kvm/jons-qemu-kvm-2) + +JIRA: https://issues.redhat.com/browse/RHEL-120119 +CVE: CVE-2025-11234 + +commit b7a1f2ca45c7865b9e98e02ae605a65fc9458ae9 +Author: Daniel P. Berrangé +Date: Tue Sep 30 12:03:15 2025 +0100 + + io: fix use after free in websocket handshake code + + If the QIOChannelWebsock object is freed while it is waiting to + complete a handshake, a GSource is leaked. This can lead to the + callback firing later on and triggering a use-after-free in the + use of the channel. This was observed in the VNC server with the + following trace from valgrind: + + ==2523108== Invalid read of size 4 + ==2523108== at 0x4054A24: vnc_disconnect_start (vnc.c:1296) + ==2523108== by 0x4054A24: vnc_client_error (vnc.c:1392) + ==2523108== by 0x4068A09: vncws_handshake_done (vnc-ws.c:105) + ==2523108== by 0x44863B4: qio_task_complete (task.c:197) + ==2523108== by 0x448343D: qio_channel_websock_handshake_io (channel-websock.c:588) + ==2523108== by 0x6EDB862: UnknownInlinedFun (gmain.c:3398) + ==2523108== by 0x6EDB862: g_main_context_dispatch_unlocked.lto_priv.0 (gmain.c:4249) + ==2523108== by 0x6EDBAE4: g_main_context_dispatch (gmain.c:4237) + ==2523108== by 0x45EC79F: glib_pollfds_poll (main-loop.c:287) + ==2523108== by 0x45EC79F: os_host_main_loop_wait (main-loop.c:310) + ==2523108== by 0x45EC79F: main_loop_wait (main-loop.c:589) + ==2523108== by 0x423A56D: qemu_main_loop (runstate.c:835) + ==2523108== by 0x454F300: qemu_default_main (main.c:37) + ==2523108== by 0x73D6574: (below main) (libc_start_call_main.h:58) + ==2523108== Address 0x57a6e0dc is 28 bytes inside a block of size 103,608 free'd + ==2523108== at 0x5F2FE43: free (vg_replace_malloc.c:989) + ==2523108== by 0x6EDC444: g_free (gmem.c:208) + ==2523108== by 0x4053F23: vnc_update_client (vnc.c:1153) + ==2523108== by 0x4053F23: vnc_refresh (vnc.c:3225) + ==2523108== by 0x4042881: dpy_refresh (console.c:880) + ==2523108== by 0x4042881: gui_update (console.c:90) + ==2523108== by 0x45EFA1B: timerlist_run_timers.part.0 (qemu-timer.c:562) + ==2523108== by 0x45EFC8F: timerlist_run_timers (qemu-timer.c:495) + ==2523108== by 0x45EFC8F: qemu_clock_run_timers (qemu-timer.c:576) + ==2523108== by 0x45EFC8F: qemu_clock_run_all_timers (qemu-timer.c:663) + ==2523108== by 0x45EC765: main_loop_wait (main-loop.c:600) + ==2523108== by 0x423A56D: qemu_main_loop (runstate.c:835) + ==2523108== by 0x454F300: qemu_default_main (main.c:37) + ==2523108== by 0x73D6574: (below main) (libc_start_call_main.h:58) + ==2523108== Block was alloc'd at + ==2523108== at 0x5F343F3: calloc (vg_replace_malloc.c:1675) + ==2523108== by 0x6EE2F81: g_malloc0 (gmem.c:133) + ==2523108== by 0x4057DA3: vnc_connect (vnc.c:3245) + ==2523108== by 0x448591B: qio_net_listener_channel_func (net-listener.c:54) + ==2523108== by 0x6EDB862: UnknownInlinedFun (gmain.c:3398) + ==2523108== by 0x6EDB862: g_main_context_dispatch_unlocked.lto_priv.0 (gmain.c:4249) + ==2523108== by 0x6EDBAE4: g_main_context_dispatch (gmain.c:4237) + ==2523108== by 0x45EC79F: glib_pollfds_poll (main-loop.c:287) + ==2523108== by 0x45EC79F: os_host_main_loop_wait (main-loop.c:310) + ==2523108== by 0x45EC79F: main_loop_wait (main-loop.c:589) + ==2523108== by 0x423A56D: qemu_main_loop (runstate.c:835) + ==2523108== by 0x454F300: qemu_default_main (main.c:37) + ==2523108== by 0x73D6574: (below main) (libc_start_call_main.h:58) + ==2523108== + + The above can be reproduced by launching QEMU with + + $ qemu-system-x86_64 -vnc localhost:0,websocket=5700 + + and then repeatedly running: + + for i in {1..100}; do + (echo -n "GET / HTTP/1.1" && sleep 0.05) | nc -w 1 localhost 5700 & + done + + CVE-2025-11234 + Reported-by: Grant Millar | Cylo + Reviewed-by: Eric Blake + Signed-off-by: Daniel P. Berrangé + +Signed-off-by: Jon Maloy +--- + include/io/channel-websock.h | 3 ++- + io/channel-websock.c | 22 ++++++++++++++++------ + 2 files changed, 18 insertions(+), 7 deletions(-) + +diff --git a/include/io/channel-websock.h b/include/io/channel-websock.h +index e180827c57..6700cf8946 100644 +--- a/include/io/channel-websock.h ++++ b/include/io/channel-websock.h +@@ -61,7 +61,8 @@ struct QIOChannelWebsock { + size_t payload_remain; + size_t pong_remain; + QIOChannelWebsockMask mask; +- guint io_tag; ++ guint hs_io_tag; /* tracking handshake task */ ++ guint io_tag; /* tracking watch task */ + Error *io_err; + gboolean io_eof; + uint8_t opcode; +diff --git a/io/channel-websock.c b/io/channel-websock.c +index ade319cb71..1b80d77f7d 100644 +--- a/io/channel-websock.c ++++ b/io/channel-websock.c +@@ -545,6 +545,7 @@ static gboolean qio_channel_websock_handshake_send(QIOChannel *ioc, + trace_qio_channel_websock_handshake_fail(ioc, error_get_pretty(err)); + qio_task_set_error(task, err); + qio_task_complete(task); ++ wioc->hs_io_tag = 0; + return FALSE; + } + +@@ -560,6 +561,7 @@ static gboolean qio_channel_websock_handshake_send(QIOChannel *ioc, + trace_qio_channel_websock_handshake_complete(ioc); + qio_task_complete(task); + } ++ wioc->hs_io_tag = 0; + return FALSE; + } + trace_qio_channel_websock_handshake_pending(ioc, G_IO_OUT); +@@ -586,6 +588,7 @@ static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc, + trace_qio_channel_websock_handshake_fail(ioc, error_get_pretty(err)); + qio_task_set_error(task, err); + qio_task_complete(task); ++ wioc->hs_io_tag = 0; + return FALSE; + } + if (ret == 0) { +@@ -597,7 +600,7 @@ static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc, + error_propagate(&wioc->io_err, err); + + trace_qio_channel_websock_handshake_reply(ioc); +- qio_channel_add_watch( ++ wioc->hs_io_tag = qio_channel_add_watch( + wioc->master, + G_IO_OUT, + qio_channel_websock_handshake_send, +@@ -906,11 +909,12 @@ void qio_channel_websock_handshake(QIOChannelWebsock *ioc, + + trace_qio_channel_websock_handshake_start(ioc); + trace_qio_channel_websock_handshake_pending(ioc, G_IO_IN); +- qio_channel_add_watch(ioc->master, +- G_IO_IN, +- qio_channel_websock_handshake_io, +- task, +- NULL); ++ ioc->hs_io_tag = qio_channel_add_watch( ++ ioc->master, ++ G_IO_IN, ++ qio_channel_websock_handshake_io, ++ task, ++ NULL); + } + + +@@ -921,6 +925,9 @@ static void qio_channel_websock_finalize(Object *obj) + buffer_free(&ioc->encinput); + buffer_free(&ioc->encoutput); + buffer_free(&ioc->rawinput); ++ if (ioc->hs_io_tag) { ++ g_source_remove(ioc->hs_io_tag); ++ } + if (ioc->io_tag) { + g_source_remove(ioc->io_tag); + } +@@ -1221,6 +1228,9 @@ static int qio_channel_websock_close(QIOChannel *ioc, + buffer_free(&wioc->encinput); + buffer_free(&wioc->encoutput); + buffer_free(&wioc->rawinput); ++ if (wioc->hs_io_tag) { ++ g_clear_handle_id(&wioc->hs_io_tag, g_source_remove); ++ } + if (wioc->io_tag) { + g_clear_handle_id(&wioc->io_tag, g_source_remove); + } +-- +2.50.1 + diff --git a/SOURCES/kvm-io-move-websock-resource-release-to-close-method.patch b/SOURCES/kvm-io-move-websock-resource-release-to-close-method.patch new file mode 100644 index 0000000..18eae5c --- /dev/null +++ b/SOURCES/kvm-io-move-websock-resource-release-to-close-method.patch @@ -0,0 +1,84 @@ +From c8540f2b17b85d539b50915f50aff4a4c817921f Mon Sep 17 00:00:00 2001 +From: Jon Maloy +Date: Tue, 4 Nov 2025 17:23:29 -0500 +Subject: [PATCH 1/2] io: move websock resource release to close method +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Jon Maloy +RH-MergeRequest: 499: io: fix use after free in websocket handshake code +RH-Jira: RHEL-120119 +RH-Acked-by: Daniel P. Berrangé +RH-Acked-by: Eric Blake +RH-Commit: [1/2] 20cbfece57dcbef55caebf33812065465de567df (redhat/rhel/src/qemu-kvm/jons-qemu-kvm-2) + +JIRA: https://issues.redhat.com/browse/RHEL-120119 +CVE: CVE-2025-11234 + +commit 322c3c4f3abee616a18b3bfe563ec29dd67eae63 +Author: Daniel P. Berrangé +Date: Tue Sep 30 11:58:35 2025 +0100 + + io: move websock resource release to close method + + The QIOChannelWebsock object releases all its resources in the + finalize callback. This is later than desired, as callers expect + to be able to call qio_channel_close() to fully close a channel + and release resources related to I/O. + + The logic in the finalize method is at most a failsafe to handle + cases where a consumer forgets to call qio_channel_close. + + This adds equivalent logic to the close method to release the + resources, using g_clear_handle_id/g_clear_pointer to be robust + against repeated invocations. The finalize method is tweaked + so that the GSource is removed before releasing the underlying + channel. + + Reviewed-by: Eric Blake + Signed-off-by: Daniel P. Berrangé + +Signed-off-by: Jon Maloy +--- + io/channel-websock.c | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +diff --git a/io/channel-websock.c b/io/channel-websock.c +index 13c94f2afe..ade319cb71 100644 +--- a/io/channel-websock.c ++++ b/io/channel-websock.c +@@ -921,13 +921,13 @@ static void qio_channel_websock_finalize(Object *obj) + buffer_free(&ioc->encinput); + buffer_free(&ioc->encoutput); + buffer_free(&ioc->rawinput); +- object_unref(OBJECT(ioc->master)); + if (ioc->io_tag) { + g_source_remove(ioc->io_tag); + } + if (ioc->io_err) { + error_free(ioc->io_err); + } ++ object_unref(OBJECT(ioc->master)); + } + + +@@ -1218,6 +1218,15 @@ static int qio_channel_websock_close(QIOChannel *ioc, + QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc); + + trace_qio_channel_websock_close(ioc); ++ buffer_free(&wioc->encinput); ++ buffer_free(&wioc->encoutput); ++ buffer_free(&wioc->rawinput); ++ if (wioc->io_tag) { ++ g_clear_handle_id(&wioc->io_tag, g_source_remove); ++ } ++ if (wioc->io_err) { ++ g_clear_pointer(&wioc->io_err, error_free); ++ } + return qio_channel_close(wioc->master, errp); + } + +-- +2.50.1 + diff --git a/SOURCES/kvm-scsi-block-Don-t-skip-callback-for-sgio-error-status.patch b/SOURCES/kvm-scsi-block-Don-t-skip-callback-for-sgio-error-status.patch new file mode 100644 index 0000000..9d857d5 --- /dev/null +++ b/SOURCES/kvm-scsi-block-Don-t-skip-callback-for-sgio-error-status.patch @@ -0,0 +1,79 @@ +From 7ec344bf9285cf854db5a643af5b9111aa47eee5 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Wed, 31 Jul 2024 14:32:05 +0200 +Subject: [PATCH 2/5] scsi-block: Don't skip callback for sgio error + status/driver_status + +RH-Author: Kevin Wolf +RH-MergeRequest: 513: scsi-block: Fix error handling with r/werror=stop +RH-Jira: RHEL-120737 +RH-Acked-by: Hanna Czenczek +RH-Acked-by: Stefan Hajnoczi +RH-Commit: [2/5] 29d87d7eb1241586a852a7f63e0ec0232a28a3ac (kmwolf/rhel-qemu-kvm) + +Instead of calling into scsi_handle_rw_error() directly from +scsi_block_sgio_complete() and skipping the normal callback, go through +the normal cleanup path by calling the callback with a positive error +value. + +The important difference here is not only that the code path is cleaner, +but that the callbacks set r->req.aiocb = NULL. If we skip setting this +and the error action is BLOCK_ERROR_ACTION_STOP, resuming the VM runs +into an assertion failure in scsi_read_data() or scsi_write_data() +because the dangling aiocb pointer is unexpected. + +Fixes: a108557bbf ("scsi: inline sg_io_sense_from_errno() into the callers.") +Buglink: https://issues.redhat.com/browse/RHEL-50000 +Signed-off-by: Kevin Wolf +Acked-by: Paolo Bonzini +Message-ID: <20240731123207.27636-3-kwolf@redhat.com> +Signed-off-by: Kevin Wolf +(cherry picked from commit 622a70161ac258e4a166a7dca4b5be267e0652d9) + +Conflicts: + hw/scsi/scsi-disk.c + +RHEL 8.10 still uses AioContext locks, so the removed code is slightly +different. The callbacks that replace it have all the necessary locking +in place. + +Signed-off-by: Kevin Wolf +--- + hw/scsi/scsi-disk.c | 14 -------------- + 1 file changed, 14 deletions(-) + +diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c +index 463c14590f..a677ad8a92 100644 +--- a/hw/scsi/scsi-disk.c ++++ b/hw/scsi/scsi-disk.c +@@ -2703,7 +2703,6 @@ static void scsi_block_sgio_complete(void *opaque, int ret) + { + SCSIBlockReq *req = (SCSIBlockReq *)opaque; + SCSIDiskReq *r = &req->req; +- SCSIDevice *s = r->req.dev; + sg_io_hdr_t *io_hdr = &req->io_header; + + if (ret == 0) { +@@ -2718,19 +2717,6 @@ static void scsi_block_sgio_complete(void *opaque, int ret) + } else { + ret = io_hdr->status; + } +- +- if (ret > 0) { +- aio_context_acquire(blk_get_aio_context(s->conf.blk)); +- if (scsi_handle_rw_error(r, ret, true)) { +- aio_context_release(blk_get_aio_context(s->conf.blk)); +- scsi_req_unref(&r->req); +- return; +- } +- aio_context_release(blk_get_aio_context(s->conf.blk)); +- +- /* Ignore error. */ +- ret = 0; +- } + } + + req->cb(req->cb_opaque, ret); +-- +2.51.1 + diff --git a/SOURCES/kvm-scsi-disk-Add-warning-comments-that-host_status-erro.patch b/SOURCES/kvm-scsi-disk-Add-warning-comments-that-host_status-erro.patch new file mode 100644 index 0000000..90ddbfc --- /dev/null +++ b/SOURCES/kvm-scsi-disk-Add-warning-comments-that-host_status-erro.patch @@ -0,0 +1,79 @@ +From e471621283748a1de44f4765be0dda5d38267e21 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Wed, 31 Jul 2024 14:32:06 +0200 +Subject: [PATCH 3/5] scsi-disk: Add warning comments that host_status errors + take a shortcut + +RH-Author: Kevin Wolf +RH-MergeRequest: 513: scsi-block: Fix error handling with r/werror=stop +RH-Jira: RHEL-120737 +RH-Acked-by: Hanna Czenczek +RH-Acked-by: Stefan Hajnoczi +RH-Commit: [3/5] 9ab9eae6eaa6465d021f438c56bcfd92dd528ffd (kmwolf/rhel-qemu-kvm) + +scsi_block_sgio_complete() has surprising behaviour in that there are +error cases in which it directly completes the request and never calls +the passed callback. In the current state of the code, this doesn't seem +to result in bugs, but with future code changes, we must be careful to +never rely on the callback doing some cleanup until this code smell is +fixed. For now, just add warnings to make people aware of the trap. + +Signed-off-by: Kevin Wolf +Acked-by: Paolo Bonzini +Message-ID: <20240731123207.27636-4-kwolf@redhat.com> +Signed-off-by: Kevin Wolf +(cherry picked from commit 8a0495624f23f8f01dfb1484f367174f3b3572e8) +Signed-off-by: Kevin Wolf +--- + hw/scsi/scsi-disk.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c +index a677ad8a92..72ce420bfc 100644 +--- a/hw/scsi/scsi-disk.c ++++ b/hw/scsi/scsi-disk.c +@@ -63,6 +63,9 @@ struct SCSIDiskClass { + /* + * Callbacks receive ret == 0 for success. Errors are represented either as + * negative errno values, or as positive SAM status codes. ++ * ++ * Beware: For errors returned in host_status, the function may directly ++ * complete the request and never call the callback. + */ + DMAIOFunc *dma_readv; + DMAIOFunc *dma_writev; +@@ -356,6 +359,7 @@ done: + } + + /* Called with AioContext lock held */ ++/* May not be called in all error cases, don't rely on cleanup here */ + static void scsi_dma_complete(void *opaque, int ret) + { + SCSIDiskReq *r = (SCSIDiskReq *)opaque; +@@ -391,6 +395,7 @@ done: + scsi_req_unref(&r->req); + } + ++/* May not be called in all error cases, don't rely on cleanup here */ + static void scsi_read_complete(void *opaque, int ret) + { + SCSIDiskReq *r = (SCSIDiskReq *)opaque; +@@ -531,6 +536,7 @@ done: + scsi_req_unref(&r->req); + } + ++/* May not be called in all error cases, don't rely on cleanup here */ + static void scsi_write_complete(void * opaque, int ret) + { + SCSIDiskReq *r = (SCSIDiskReq *)opaque; +@@ -2706,6 +2712,7 @@ static void scsi_block_sgio_complete(void *opaque, int ret) + sg_io_hdr_t *io_hdr = &req->io_header; + + if (ret == 0) { ++ /* FIXME This skips calling req->cb() and any cleanup in it */ + if (io_hdr->host_status != SCSI_HOST_OK) { + scsi_req_complete_failed(&r->req, io_hdr->host_status); + scsi_req_unref(&r->req); +-- +2.51.1 + diff --git a/SOURCES/kvm-scsi-disk-Always-report-RESERVATION_CONFLICT-to-gues.patch b/SOURCES/kvm-scsi-disk-Always-report-RESERVATION_CONFLICT-to-gues.patch new file mode 100644 index 0000000..94134b7 --- /dev/null +++ b/SOURCES/kvm-scsi-disk-Always-report-RESERVATION_CONFLICT-to-gues.patch @@ -0,0 +1,105 @@ +From 1347455e52ebe93e52c56d1d1b3107f35242dbcb Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Wed, 31 Jul 2024 14:32:07 +0200 +Subject: [PATCH 4/5] scsi-disk: Always report RESERVATION_CONFLICT to guest + +RH-Author: Kevin Wolf +RH-MergeRequest: 513: scsi-block: Fix error handling with r/werror=stop +RH-Jira: RHEL-120737 +RH-Acked-by: Hanna Czenczek +RH-Acked-by: Stefan Hajnoczi +RH-Commit: [4/5] 651265ef6da28e1371b1f945921004f84784a840 (kmwolf/rhel-qemu-kvm) + +In the case of scsi-block, RESERVATION_CONFLICT is not a backend error, +but indicates that the guest tried to make a request that it isn't +allowed to execute. Pass the error to the guest so that it can decide +what to do with it. + +Without this, if we stop the VM in response to a RESERVATION_CONFLICT +(as is the default policy in management software such as oVirt or +KubeVirt), it can happen that the VM cannot be resumed any more because +every attempt to resume it immediately runs into the same error and +stops the VM again. + +One case that expects RESERVATION_CONFLICT errors to be visible in the +guest is running the validation tests in Windows 2019's Failover Cluster +Manager, which intentionally tries to execute invalid requests to see if +they are properly rejected. + +Buglink: https://issues.redhat.com/browse/RHEL-50000 +Signed-off-by: Kevin Wolf +Acked-by: Paolo Bonzini +Message-ID: <20240731123207.27636-5-kwolf@redhat.com> +Signed-off-by: Kevin Wolf +(cherry picked from commit 9da6bd39f92434f55573acd017841b195c60188f) +Signed-off-by: Kevin Wolf +--- + hw/scsi/scsi-disk.c | 35 ++++++++++++++++++++++++++++++----- + 1 file changed, 30 insertions(+), 5 deletions(-) + +diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c +index 72ce420bfc..4e70cf1342 100644 +--- a/hw/scsi/scsi-disk.c ++++ b/hw/scsi/scsi-disk.c +@@ -199,7 +199,7 @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int ret, bool acct_failed) + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + SCSIDiskClass *sdc = (SCSIDiskClass *) object_get_class(OBJECT(s)); + SCSISense sense = SENSE_CODE(NO_SENSE); +- int error = 0; ++ int error; + bool req_has_sense = false; + BlockErrorAction action; + int status; +@@ -210,11 +210,35 @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int ret, bool acct_failed) + } else { + /* A passthrough command has completed with nonzero status. */ + status = ret; +- if (status == CHECK_CONDITION) { ++ switch (status) { ++ case CHECK_CONDITION: + req_has_sense = true; + error = scsi_sense_buf_to_errno(r->req.sense, sizeof(r->req.sense)); +- } else { ++ break; ++ case RESERVATION_CONFLICT: ++ /* ++ * Don't apply the error policy, always report to the guest. ++ * ++ * This is a passthrough code path, so it's not a backend error, but ++ * a response to an invalid guest request. ++ * ++ * Windows Failover Cluster validation intentionally sends invalid ++ * requests to verify that reservations work as intended. It is ++ * crucial that it sees the resulting errors. ++ * ++ * Treating a reservation conflict as a guest-side error is obvious ++ * when a pr-manager is in use. Without one, the situation is less ++ * clear, but there might be nothing that can be fixed on the host ++ * (like in the above example), and we don't want to be stuck in a ++ * loop where resuming the VM and retrying the request immediately ++ * stops it again. So always reporting is still the safer option in ++ * this case, too. ++ */ ++ error = 0; ++ break; ++ default: + error = EINVAL; ++ break; + } + } + +@@ -224,8 +248,9 @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int ret, bool acct_failed) + * are usually retried immediately, so do not post them to QMP and + * do not account them as failed I/O. + */ +- if (req_has_sense && +- scsi_sense_buf_is_guest_recoverable(r->req.sense, sizeof(r->req.sense))) { ++ if (!error || (req_has_sense && ++ scsi_sense_buf_is_guest_recoverable(r->req.sense, ++ sizeof(r->req.sense)))) { + action = BLOCK_ERROR_ACTION_REPORT; + acct_failed = false; + } else { +-- +2.51.1 + diff --git a/SOURCES/kvm-scsi-disk-Apply-error-policy-for-host_status-errors-.patch b/SOURCES/kvm-scsi-disk-Apply-error-policy-for-host_status-errors-.patch new file mode 100644 index 0000000..789fde6 --- /dev/null +++ b/SOURCES/kvm-scsi-disk-Apply-error-policy-for-host_status-errors-.patch @@ -0,0 +1,155 @@ +From 5e021a147a211ef2a75e9293f5f54951e1c87d7f Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Mon, 7 Apr 2025 17:59:49 +0200 +Subject: [PATCH 5/5] scsi-disk: Apply error policy for host_status errors + again + +RH-Author: Kevin Wolf +RH-MergeRequest: 513: scsi-block: Fix error handling with r/werror=stop +RH-Jira: RHEL-120737 +RH-Acked-by: Hanna Czenczek +RH-Acked-by: Stefan Hajnoczi +RH-Commit: [5/5] d4316323cf0d28c42e06a485b9569cc548494337 (kmwolf/rhel-qemu-kvm) + +Originally, all failed SG_IO requests called scsi_handle_rw_error() to +apply the configured error policy. However, commit f3126d65, which was +supposed to be a mere refactoring for scsi-disk.c, broke this and +accidentally completed the SCSI request without considering the error +policy any more if the error was signalled in the host_status field. + +Apart from the commit message not describing the change as intended, +errors indicated in host_status are also obviously backend errors and +not something the guest must deal with independently of the error +policy. + +This behaviour means that some recoverable errors (such as a path error +in multipath configurations) were reported to the guest anyway, which +might not expect it and might consider its disk broken. + +Make sure that we apply the error policy again for host_status errors, +too. This addresses an existing FIXME comment and allows us to remove +some comments warning that callbacks weren't always called. With this +fix, they are called in all cases again. + +The return value passed to the request callback doesn't have more free +values that could be used to indicate host_status errors as well as SAM +status codes and negative errno. Store the value in the host_status +field of the SCSIRequest instead and use -ENODEV as the return value (if +a path hasn't been reachable for a while, blk_aio_ioctl() will return +-ENODEV instead of just setting host_status, so just reuse it here - +it's not necessarily entirely accurate, but it's as good as any errno). + +Cc: qemu-stable@nongnu.org +Fixes: f3126d65b393 ('scsi: move host_status handling into SCSI drivers') +Signed-off-by: Kevin Wolf +Message-ID: <20250407155949.44736-1-kwolf@redhat.com> +Reviewed-by: Stefan Hajnoczi +Reviewed-by: Hanna Czenczek +Signed-off-by: Kevin Wolf +(cherry picked from commit 61b6d9b749ba233784c7214cfe9585ea321159dc) +Signed-off-by: Kevin Wolf +--- + hw/scsi/scsi-disk.c | 39 +++++++++++++++++++++++++-------------- + 1 file changed, 25 insertions(+), 14 deletions(-) + +diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c +index 4e70cf1342..46fbee1a64 100644 +--- a/hw/scsi/scsi-disk.c ++++ b/hw/scsi/scsi-disk.c +@@ -62,10 +62,9 @@ struct SCSIDiskClass { + SCSIDeviceClass parent_class; + /* + * Callbacks receive ret == 0 for success. Errors are represented either as +- * negative errno values, or as positive SAM status codes. +- * +- * Beware: For errors returned in host_status, the function may directly +- * complete the request and never call the callback. ++ * negative errno values, or as positive SAM status codes. For host_status ++ * errors, the function passes ret == -ENODEV and sets the host_status field ++ * of the SCSIRequest. + */ + DMAIOFunc *dma_readv; + DMAIOFunc *dma_writev; +@@ -199,11 +198,26 @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int ret, bool acct_failed) + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + SCSIDiskClass *sdc = (SCSIDiskClass *) object_get_class(OBJECT(s)); + SCSISense sense = SENSE_CODE(NO_SENSE); ++ int16_t host_status; + int error; + bool req_has_sense = false; + BlockErrorAction action; + int status; + ++ /* ++ * host_status should only be set for SG_IO requests that came back with a ++ * host_status error in scsi_block_sgio_complete(). This error path passes ++ * -ENODEV as the return value. ++ * ++ * Reset host_status in the request because we may still want to complete ++ * the request successfully with the 'stop' or 'ignore' error policy. ++ */ ++ host_status = r->req.host_status; ++ if (host_status != -1) { ++ assert(ret == -ENODEV); ++ r->req.host_status = -1; ++ } ++ + if (ret < 0) { + status = scsi_sense_from_errno(-ret, &sense); + error = -ret; +@@ -263,6 +277,10 @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int ret, bool acct_failed) + if (acct_failed) { + block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); + } ++ if (host_status != -1) { ++ scsi_req_complete_failed(&r->req, host_status); ++ return true; ++ } + if (req_has_sense) { + sdc->update_sense(&r->req); + } else if (status == CHECK_CONDITION) { +@@ -384,7 +402,6 @@ done: + } + + /* Called with AioContext lock held */ +-/* May not be called in all error cases, don't rely on cleanup here */ + static void scsi_dma_complete(void *opaque, int ret) + { + SCSIDiskReq *r = (SCSIDiskReq *)opaque; +@@ -420,7 +437,6 @@ done: + scsi_req_unref(&r->req); + } + +-/* May not be called in all error cases, don't rely on cleanup here */ + static void scsi_read_complete(void *opaque, int ret) + { + SCSIDiskReq *r = (SCSIDiskReq *)opaque; +@@ -561,7 +577,6 @@ done: + scsi_req_unref(&r->req); + } + +-/* May not be called in all error cases, don't rely on cleanup here */ + static void scsi_write_complete(void * opaque, int ret) + { + SCSIDiskReq *r = (SCSIDiskReq *)opaque; +@@ -2737,14 +2752,10 @@ static void scsi_block_sgio_complete(void *opaque, int ret) + sg_io_hdr_t *io_hdr = &req->io_header; + + if (ret == 0) { +- /* FIXME This skips calling req->cb() and any cleanup in it */ + if (io_hdr->host_status != SCSI_HOST_OK) { +- scsi_req_complete_failed(&r->req, io_hdr->host_status); +- scsi_req_unref(&r->req); +- return; +- } +- +- if (io_hdr->driver_status & SG_ERR_DRIVER_TIMEOUT) { ++ r->req.host_status = io_hdr->host_status; ++ ret = -ENODEV; ++ } else if (io_hdr->driver_status & SG_ERR_DRIVER_TIMEOUT) { + ret = BUSY; + } else { + ret = io_hdr->status; +-- +2.51.1 + diff --git a/SOURCES/kvm-scsi-disk-Use-positive-return-value-for-status-in-dm.patch b/SOURCES/kvm-scsi-disk-Use-positive-return-value-for-status-in-dm.patch new file mode 100644 index 0000000..4f67637 --- /dev/null +++ b/SOURCES/kvm-scsi-disk-Use-positive-return-value-for-status-in-dm.patch @@ -0,0 +1,125 @@ +From 0a4b86ebcaf5c2e6623ec49374a0dea500b448b7 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Wed, 31 Jul 2024 14:32:04 +0200 +Subject: [PATCH 1/5] scsi-disk: Use positive return value for status in + dma_readv/writev + +RH-Author: Kevin Wolf +RH-MergeRequest: 513: scsi-block: Fix error handling with r/werror=stop +RH-Jira: RHEL-120737 +RH-Acked-by: Hanna Czenczek +RH-Acked-by: Stefan Hajnoczi +RH-Commit: [1/5] 4e93ed1eb1d760b95b93a5a5709725c927afd3c9 (kmwolf/rhel-qemu-kvm) + +In some error cases, scsi_block_sgio_complete() never calls the passed +callback, but directly completes the request. This leads to bugs because +its error paths are not exact copies of what the callback would normally +do. + +In preparation to fix this, allow passing positive return values to the +callbacks that represent the status code that should be used to complete +the request. + +scsi_handle_rw_error() already handles positive values for its ret +parameter because scsi_block_sgio_complete() calls directly into it. + +Signed-off-by: Kevin Wolf +Acked-by: Paolo Bonzini +Message-ID: <20240731123207.27636-2-kwolf@redhat.com> +Signed-off-by: Kevin Wolf +(cherry picked from commit cfe0880835cd364b590ffd27ef8dbd2ad8838bc5) +Signed-off-by: Kevin Wolf +--- + hw/scsi/scsi-disk.c | 21 ++++++++++++++------- + 1 file changed, 14 insertions(+), 7 deletions(-) + +diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c +index c8109a673e..463c14590f 100644 +--- a/hw/scsi/scsi-disk.c ++++ b/hw/scsi/scsi-disk.c +@@ -60,6 +60,10 @@ OBJECT_DECLARE_TYPE(SCSIDiskState, SCSIDiskClass, SCSI_DISK_BASE) + + struct SCSIDiskClass { + SCSIDeviceClass parent_class; ++ /* ++ * Callbacks receive ret == 0 for success. Errors are represented either as ++ * negative errno values, or as positive SAM status codes. ++ */ + DMAIOFunc *dma_readv; + DMAIOFunc *dma_writev; + bool (*need_fua_emulation)(SCSICommand *cmd); +@@ -258,7 +262,7 @@ static bool scsi_disk_req_check_error(SCSIDiskReq *r, int ret, bool acct_failed) + return true; + } + +- if (ret < 0) { ++ if (ret != 0) { + return scsi_handle_rw_error(r, ret, acct_failed); + } + +@@ -334,7 +338,7 @@ static void scsi_write_do_fua(SCSIDiskReq *r) + static void scsi_dma_complete_noio(SCSIDiskReq *r, int ret) + { + assert(r->req.aiocb == NULL); +- if (scsi_disk_req_check_error(r, ret, false)) { ++ if (scsi_disk_req_check_error(r, ret, ret > 0)) { + goto done; + } + +@@ -360,9 +364,10 @@ static void scsi_dma_complete(void *opaque, int ret) + assert(r->req.aiocb != NULL); + r->req.aiocb = NULL; + ++ /* ret > 0 is accounted for in scsi_disk_req_check_error() */ + if (ret < 0) { + block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); +- } else { ++ } else if (ret == 0) { + block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); + } + scsi_dma_complete_noio(r, ret); +@@ -373,7 +378,7 @@ static void scsi_read_complete_noio(SCSIDiskReq *r, int ret) + uint32_t n; + + assert(r->req.aiocb == NULL); +- if (scsi_disk_req_check_error(r, ret, false)) { ++ if (scsi_disk_req_check_error(r, ret, ret > 0)) { + goto done; + } + +@@ -396,9 +401,10 @@ static void scsi_read_complete(void *opaque, int ret) + assert(r->req.aiocb != NULL); + r->req.aiocb = NULL; + ++ /* ret > 0 is accounted for in scsi_disk_req_check_error() */ + if (ret < 0) { + block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); +- } else { ++ } else if (ret == 0) { + block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); + trace_scsi_disk_read_complete(r->req.tag, r->qiov.size); + } +@@ -505,7 +511,7 @@ static void scsi_write_complete_noio(SCSIDiskReq *r, int ret) + uint32_t n; + + assert (r->req.aiocb == NULL); +- if (scsi_disk_req_check_error(r, ret, false)) { ++ if (scsi_disk_req_check_error(r, ret, ret > 0)) { + goto done; + } + +@@ -535,9 +541,10 @@ static void scsi_write_complete(void * opaque, int ret) + assert (r->req.aiocb != NULL); + r->req.aiocb = NULL; + ++ /* ret > 0 is accounted for in scsi_disk_req_check_error() */ + if (ret < 0) { + block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); +- } else { ++ } else if (ret == 0) { + block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); + } + scsi_write_complete_noio(r, ret); +-- +2.51.1 + diff --git a/SPECS/qemu-kvm.spec b/SPECS/qemu-kvm.spec index 3596d1a..e7eecc6 100644 --- a/SPECS/qemu-kvm.spec +++ b/SPECS/qemu-kvm.spec @@ -83,7 +83,7 @@ Obsoletes: %1-rhev <= %{epoch}:%{version}-%{release} Summary: QEMU is a machine emulator and virtualizer Name: qemu-kvm Version: 6.2.0 -Release: 53%{?rcrel}%{?dist}.4 +Release: 53%{?rcrel}%{?dist}.6 # Epoch because we pushed a qemu-1.0 package. AIUI this can't ever be dropped Epoch: 15 License: GPLv2 and GPLv2+ and CC-BY @@ -903,6 +903,20 @@ Patch370: kvm-target-s390x-filter-deprecated-properties-based-on-m.patch Patch371: kvm-target-s390x-move-deprecated-props-to-CpuModelExpans.patch # For RHEL-88701 - [RHEL 8.10 qemu] KVM - Live migration of guest fails from z16-z17 [rhel-8.10.z] Patch372: kvm-redhat-Adjust-indentation-in-qapi-machine-target.jso.patch +# For RHEL-120119 - CVE-2025-11234 virt:rhel/qemu-kvm: VNC WebSocket handshake use-after-free [rhel-8.10.z] +Patch373: kvm-io-move-websock-resource-release-to-close-method.patch +# For RHEL-120119 - CVE-2025-11234 virt:rhel/qemu-kvm: VNC WebSocket handshake use-after-free [rhel-8.10.z] +Patch374: kvm-io-fix-use-after-free-in-websocket-handshake-code.patch +# For RHEL-120737 - qemu hit io error and crash scsi-disk.c:554: scsi_write_data: Assertion `r->req.aiocb == NULL' failed +Patch375: kvm-scsi-disk-Use-positive-return-value-for-status-in-dm.patch +# For RHEL-120737 - qemu hit io error and crash scsi-disk.c:554: scsi_write_data: Assertion `r->req.aiocb == NULL' failed +Patch376: kvm-scsi-block-Don-t-skip-callback-for-sgio-error-status.patch +# For RHEL-120737 - qemu hit io error and crash scsi-disk.c:554: scsi_write_data: Assertion `r->req.aiocb == NULL' failed +Patch377: kvm-scsi-disk-Add-warning-comments-that-host_status-erro.patch +# For RHEL-120737 - qemu hit io error and crash scsi-disk.c:554: scsi_write_data: Assertion `r->req.aiocb == NULL' failed +Patch378: kvm-scsi-disk-Always-report-RESERVATION_CONFLICT-to-gues.patch +# For RHEL-120737 - qemu hit io error and crash scsi-disk.c:554: scsi_write_data: Assertion `r->req.aiocb == NULL' failed +Patch379: kvm-scsi-disk-Apply-error-policy-for-host_status-errors-.patch BuildRequires: wget BuildRequires: rpm-build @@ -2072,6 +2086,21 @@ sh %{_sysconfdir}/sysconfig/modules/kvm.modules &> /dev/null || : %changelog +* Tue Dec 02 2025 Jon Maloy - 6.2.0-53.el8.6 +- kvm-scsi-disk-Use-positive-return-value-for-status-in-dm.patch [RHEL-120737] +- kvm-scsi-block-Don-t-skip-callback-for-sgio-error-status.patch [RHEL-120737] +- kvm-scsi-disk-Add-warning-comments-that-host_status-erro.patch [RHEL-120737] +- kvm-scsi-disk-Always-report-RESERVATION_CONFLICT-to-gues.patch [RHEL-120737] +- kvm-scsi-disk-Apply-error-policy-for-host_status-errors-.patch [RHEL-120737] +- Resolves: RHEL-120737 + (qemu hit io error and crash scsi-disk.c:554: scsi_write_data: Assertion `r->req.aiocb == NULL' failed) + +* Wed Nov 19 2025 Jon Maloy - 6.2.0-53.el8.5 +- kvm-io-move-websock-resource-release-to-close-method.patch [RHEL-120119] +- kvm-io-fix-use-after-free-in-websocket-handshake-code.patch [RHEL-120119] +- Resolves: RHEL-120119 + (CVE-2025-11234 virt:rhel/qemu-kvm: VNC WebSocket handshake use-after-free [rhel-8.10.z]) + * Mon May 05 2025 Jon Maloy - 6.2.0-53.el8.4 - kvm-target-s390x-report-deprecated-props-in-cpu-model-ex.patch [RHEL-88701] - kvm-target-s390x-flag-te-and-cte-as-deprecated.patch [RHEL-88701]