* [Stable-7.2.14 01/40] qapi/qom: Document feature unstable of @x-vfio-user-server
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
@ 2024-09-06 5:15 ` Michael Tokarev
2024-09-06 5:15 ` [Stable-7.2.14 02/40] target/arm: Use float_status copy in sme_fmopa_s Michael Tokarev
` (38 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:15 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Markus Armbruster, Elena Ufimtseva, John G Johnson,
Jagannathan Raman, John Snow, Michael Tokarev
From: Markus Armbruster <armbru@redhat.com>
Commit 8f9a9259d32c added ObjectType member @x-vfio-user-server with
feature unstable, but neglected to explain why it is unstable. Do
that now.
Fixes: 8f9a9259d32c (vfio-user: define vfio-user-server object)
Cc: Elena Ufimtseva <elena.ufimtseva@oracle.com>
Cc: John G Johnson <john.g.johnson@oracle.com>
Cc: Jagannathan Raman <jag.raman@oracle.com>
Cc: qemu-stable@nongnu.org
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-ID: <20240703095310.1242102-1-armbru@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
[Indentation fixed]
(cherry picked from commit 3becc939081097ccfed6968771f33d65ce8551eb)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
(mjt: fix context and fix indentation for 7.2.x)
foo
diff --git a/qapi/qom.json b/qapi/qom.json
index 30e76653ad..694bb81948 100644
--- a/qapi/qom.json
+++ b/qapi/qom.json
@@ -860,7 +860,8 @@
# @ObjectType:
#
# Features:
-# @unstable: Member @x-remote-object is experimental.
+# @unstable: Members @x-remote-object and @x-vfio-user-server are
+# experimental.
#
# Since: 6.0
##
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 02/40] target/arm: Use float_status copy in sme_fmopa_s
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
2024-09-06 5:15 ` [Stable-7.2.14 01/40] qapi/qom: Document feature unstable of @x-vfio-user-server Michael Tokarev
@ 2024-09-06 5:15 ` Michael Tokarev
2024-09-06 5:15 ` [Stable-7.2.14 03/40] target/arm: Use FPST_F16 for SME FMOPA (widening) Michael Tokarev
` (37 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:15 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Daniyal Khan, Richard Henderson,
Philippe Mathieu-Daudé, Alex Bennée, Peter Maydell,
Michael Tokarev
From: Daniyal Khan <danikhan632@gmail.com>
We made a copy above because the fp exception flags
are not propagated back to the FPST register, but
then failed to use the copy.
Cc: qemu-stable@nongnu.org
Fixes: 558e956c719 ("target/arm: Implement FMOPA, FMOPS (non-widening)")
Signed-off-by: Daniyal Khan <danikhan632@gmail.com>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Message-id: 20240717060149.204788-2-richard.henderson@linaro.org
[rth: Split from a larger patch]
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit 31d93fedf41c24b0badb38cd9317590d1ef74e37)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/target/arm/sme_helper.c b/target/arm/sme_helper.c
index d592c78ec9..e55bc51d69 100644
--- a/target/arm/sme_helper.c
+++ b/target/arm/sme_helper.c
@@ -949,7 +949,7 @@ void HELPER(sme_fmopa_s)(void *vza, void *vzn, void *vzm, void *vpn,
if (pb & 1) {
uint32_t *a = vza_row + H1_4(col);
uint32_t *m = vzm + H1_4(col);
- *a = float32_muladd(n, *m, *a, 0, vst);
+ *a = float32_muladd(n, *m, *a, 0, &fpst);
}
col += 4;
pb >>= 4;
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 03/40] target/arm: Use FPST_F16 for SME FMOPA (widening)
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
2024-09-06 5:15 ` [Stable-7.2.14 01/40] qapi/qom: Document feature unstable of @x-vfio-user-server Michael Tokarev
2024-09-06 5:15 ` [Stable-7.2.14 02/40] target/arm: Use float_status copy in sme_fmopa_s Michael Tokarev
@ 2024-09-06 5:15 ` Michael Tokarev
2024-09-06 5:15 ` [Stable-7.2.14 04/40] hw/nvme: fix memory leak in nvme_dsm Michael Tokarev
` (36 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:15 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Richard Henderson, Daniyal Khan, Alex Bennée,
Peter Maydell, Michael Tokarev
From: Richard Henderson <richard.henderson@linaro.org>
This operation has float16 inputs and thus must use
the FZ16 control not the FZ control.
Cc: qemu-stable@nongnu.org
Fixes: 3916841ac75 ("target/arm: Implement FMOPA, FMOPS (widening)")
Reported-by: Daniyal Khan <danikhan632@gmail.com>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Message-id: 20240717060149.204788-3-richard.henderson@linaro.org
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2374
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit 207d30b5fdb5b45a36f26eefcf52fe2c1714dd4f)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/target/arm/translate-sme.c b/target/arm/translate-sme.c
index 65f8495bdd..8cce34e117 100644
--- a/target/arm/translate-sme.c
+++ b/target/arm/translate-sme.c
@@ -340,6 +340,7 @@ static bool do_outprod(DisasContext *s, arg_op *a, MemOp esz,
}
static bool do_outprod_fpst(DisasContext *s, arg_op *a, MemOp esz,
+ ARMFPStatusFlavour e_fpst,
gen_helper_gvec_5_ptr *fn)
{
int svl = streaming_vec_reg_size(s);
@@ -355,7 +356,7 @@ static bool do_outprod_fpst(DisasContext *s, arg_op *a, MemOp esz,
zm = vec_full_reg_ptr(s, a->zm);
pn = pred_full_reg_ptr(s, a->pn);
pm = pred_full_reg_ptr(s, a->pm);
- fpst = fpstatus_ptr(FPST_FPCR);
+ fpst = fpstatus_ptr(e_fpst);
fn(za, zn, zm, pn, pm, fpst, tcg_constant_i32(desc));
@@ -367,9 +368,12 @@ static bool do_outprod_fpst(DisasContext *s, arg_op *a, MemOp esz,
return true;
}
-TRANS_FEAT(FMOPA_h, aa64_sme, do_outprod_fpst, a, MO_32, gen_helper_sme_fmopa_h)
-TRANS_FEAT(FMOPA_s, aa64_sme, do_outprod_fpst, a, MO_32, gen_helper_sme_fmopa_s)
-TRANS_FEAT(FMOPA_d, aa64_sme_f64f64, do_outprod_fpst, a, MO_64, gen_helper_sme_fmopa_d)
+TRANS_FEAT(FMOPA_h, aa64_sme, do_outprod_fpst, a,
+ MO_32, FPST_FPCR_F16, gen_helper_sme_fmopa_h)
+TRANS_FEAT(FMOPA_s, aa64_sme, do_outprod_fpst, a,
+ MO_32, FPST_FPCR, gen_helper_sme_fmopa_s)
+TRANS_FEAT(FMOPA_d, aa64_sme_f64f64, do_outprod_fpst, a,
+ MO_64, FPST_FPCR, gen_helper_sme_fmopa_d)
/* TODO: FEAT_EBF16 */
TRANS_FEAT(BFMOPA, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_bfmopa)
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 04/40] hw/nvme: fix memory leak in nvme_dsm
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (2 preceding siblings ...)
2024-09-06 5:15 ` [Stable-7.2.14 03/40] target/arm: Use FPST_F16 for SME FMOPA (widening) Michael Tokarev
@ 2024-09-06 5:15 ` Michael Tokarev
2024-09-06 5:15 ` [Stable-7.2.14 05/40] hw/cxl/cxl-host: Fix segmentation fault when getting cxl-fmw property Michael Tokarev
` (35 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:15 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Zheyu Ma, Klaus Jensen, Michael Tokarev
From: Zheyu Ma <zheyuma97@gmail.com>
The allocated memory to hold LBA ranges leaks in the nvme_dsm function. This
happens because the allocated memory for iocb->range is not freed in all
error handling paths.
Fix this by adding a free to ensure that the allocated memory is properly freed.
ASAN log:
==3075137==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 480 byte(s) in 6 object(s) allocated from:
#0 0x55f1f8a0eddd in malloc llvm/compiler-rt/lib/asan/asan_malloc_linux.cpp:129:3
#1 0x7f531e0f6738 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x5e738)
#2 0x55f1faf1f091 in blk_aio_get block/block-backend.c:2583:12
#3 0x55f1f945c74b in nvme_dsm hw/nvme/ctrl.c:2609:30
#4 0x55f1f945831b in nvme_io_cmd hw/nvme/ctrl.c:4470:16
#5 0x55f1f94561b7 in nvme_process_sq hw/nvme/ctrl.c:7039:29
Cc: qemu-stable@nongnu.org
Fixes: d7d1474fd85d ("hw/nvme: reimplement dsm to allow cancellation")
Signed-off-by: Zheyu Ma <zheyuma97@gmail.com>
Reviewed-by: Klaus Jensen <k.jensen@samsung.com>
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
(cherry picked from commit c510fe78f1b7c966524489d6ba752107423b20c8)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index 027d67f10b..ed56ad40b3 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -2465,6 +2465,7 @@ next:
done:
iocb->aiocb = NULL;
iocb->common.cb(iocb->common.opaque, iocb->ret);
+ g_free(iocb->range);
qemu_aio_unref(iocb);
}
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 05/40] hw/cxl/cxl-host: Fix segmentation fault when getting cxl-fmw property
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (3 preceding siblings ...)
2024-09-06 5:15 ` [Stable-7.2.14 04/40] hw/nvme: fix memory leak in nvme_dsm Michael Tokarev
@ 2024-09-06 5:15 ` Michael Tokarev
2024-09-06 5:15 ` [Stable-7.2.14 06/40] intel_iommu: fix FRCD construction macro Michael Tokarev
` (34 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:15 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Zhao Liu, Li Zhijian, Xingtao Yao, Jonathan Cameron,
Michael S . Tsirkin, Michael Tokarev
From: Zhao Liu <zhao1.liu@intel.com>
QEMU crashes (Segmentation fault) when getting cxl-fmw property via
qmp:
(QEMU) qom-get path=machine property=cxl-fmw
This issue is caused by accessing wrong callback (opaque) type in
machine_get_cfmw().
cxl_machine_init() sets the callback as `CXLState *` type but
machine_get_cfmw() treats the callback as
`CXLFixedMemoryWindowOptionsList **`.
Fix this error by casting opaque to `CXLState *` type in
machine_get_cfmw().
Fixes: 03b39fcf64bc ("hw/cxl: Make the CXL fixed memory window setup a machine parameter.")
Signed-off-by: Zhao Liu <zhao1.liu@intel.com>
Reviewed-by: Li Zhijian <lizhijian@fujitsu.com>
Reviewed-by: Xingtao Yao <yaoxt.fnst@fujitsu.com>
Link: https://lore.kernel.org/r/20240704093404.1848132-1-zhao1.liu@linux.intel.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Message-Id: <20240705113956.941732-2-Jonathan.Cameron@huawei.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
(cherry picked from commit a207d5f87d66f7933b50677e047498fc4af63e1f)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/hw/cxl/cxl-host.c b/hw/cxl/cxl-host.c
index 0fc3e57138..3253874322 100644
--- a/hw/cxl/cxl-host.c
+++ b/hw/cxl/cxl-host.c
@@ -282,7 +282,8 @@ static void machine_set_cxl(Object *obj, Visitor *v, const char *name,
static void machine_get_cfmw(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
- CXLFixedMemoryWindowOptionsList **list = opaque;
+ CXLState *state = opaque;
+ CXLFixedMemoryWindowOptionsList **list = &state->cfmw_list;
visit_type_CXLFixedMemoryWindowOptionsList(v, name, list, errp);
}
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 06/40] intel_iommu: fix FRCD construction macro
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (4 preceding siblings ...)
2024-09-06 5:15 ` [Stable-7.2.14 05/40] hw/cxl/cxl-host: Fix segmentation fault when getting cxl-fmw property Michael Tokarev
@ 2024-09-06 5:15 ` Michael Tokarev
2024-09-06 5:15 ` [Stable-7.2.14 07/40] target/i386: do not crash if microvm guest uses SGX CPUID leaves Michael Tokarev
` (33 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:15 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Clément Mathieu--Drif, Yi Liu, Zhenzhong Duan,
Minwoo Im, Michael S . Tsirkin, Michael Tokarev
From: Clément Mathieu--Drif <clement.mathieu--drif@eviden.com>
The constant must be unsigned, otherwise the two's complement
overrides the other fields when a PASID is present.
Fixes: 1b2b12376c8a ("intel-iommu: PASID support")
Signed-off-by: Clément Mathieu--Drif <clement.mathieu--drif@eviden.com>
Reviewed-by: Yi Liu <yi.l.liu@intel.com>
Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Reviewed-by: Minwoo Im <minwoo.im@samsung.com>
Message-Id: <20240709142557.317271-2-clement.mathieu--drif@eviden.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
(cherry picked from commit a3c8d7e38550c3d5a46e6fa94ffadfa625a4861d)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
(Mjt: context fix)
diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h
index e4d43ce48c..830e319e34 100644
--- a/hw/i386/intel_iommu_internal.h
+++ b/hw/i386/intel_iommu_internal.h
@@ -267,7 +267,7 @@
/* For the low 64-bit of 128-bit */
#define VTD_FRCD_FI(val) ((val) & ~0xfffULL)
#define VTD_FRCD_PV(val) (((val) & 0xffffULL) << 40)
-#define VTD_FRCD_PP(val) (((val) & 0x1) << 31)
+#define VTD_FRCD_PP(val) (((val) & 0x1ULL) << 31)
/* DMA Remapping Fault Conditions */
typedef enum VTDFaultReason {
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 07/40] target/i386: do not crash if microvm guest uses SGX CPUID leaves
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (5 preceding siblings ...)
2024-09-06 5:15 ` [Stable-7.2.14 06/40] intel_iommu: fix FRCD construction macro Michael Tokarev
@ 2024-09-06 5:15 ` Michael Tokarev
2024-09-06 5:15 ` [Stable-7.2.14 08/40] chardev/char-win-stdio.c: restore old console mode Michael Tokarev
` (32 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:15 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Paolo Bonzini, Michael Tokarev
From: Paolo Bonzini <pbonzini@redhat.com>
sgx_epc_get_section assumes a PC platform is in use:
bool sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size)
{
PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
However, sgx_epc_get_section is called by CPUID regardless of whether
SGX state has been initialized or which platform is in use. Check
whether the machine has the right QOM class and if not behave as if
there are no EPC sections.
Fixes: 1dec2e1f19f ("i386: Update SGX CPUID info according to hardware/KVM/user input", 2021-09-30)
Cc: qemu-stable@nongnu.org
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2142
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 13be929aff804581b21e69087a9caf3698fd5c3c)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/hw/i386/sgx.c b/hw/i386/sgx.c
index 09d9c7c73d..f64987c6dd 100644
--- a/hw/i386/sgx.c
+++ b/hw/i386/sgx.c
@@ -268,10 +268,12 @@ void hmp_info_sgx(Monitor *mon, const QDict *qdict)
bool sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size)
{
- PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
+ PCMachineState *pcms =
+ (PCMachineState *)object_dynamic_cast(qdev_get_machine(),
+ TYPE_PC_MACHINE);
SGXEPCDevice *epc;
- if (pcms->sgx_epc.size == 0 || pcms->sgx_epc.nr_sections <= section_nr) {
+ if (!pcms || pcms->sgx_epc.size == 0 || pcms->sgx_epc.nr_sections <= section_nr) {
return true;
}
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 08/40] chardev/char-win-stdio.c: restore old console mode
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (6 preceding siblings ...)
2024-09-06 5:15 ` [Stable-7.2.14 07/40] target/i386: do not crash if microvm guest uses SGX CPUID leaves Michael Tokarev
@ 2024-09-06 5:15 ` Michael Tokarev
2024-09-06 5:15 ` [Stable-7.2.14 09/40] hw/intc/loongson_ipi: Access memory in little endian Michael Tokarev
` (31 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:15 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, songziming, Marc-André Lureau, Michael Tokarev
From: songziming <s.ziming@hotmail.com>
If I use `-serial stdio` on Windows, after QEMU exits, the terminal
could not handle arrow keys and tab any more. Because stdio backend
on Windows sets console mode to virtual terminal input when starts,
but does not restore the old mode when finalize.
This small patch saves the old console mode and set it back.
Signed-off-by: Ziming Song <s.ziming@hotmail.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-ID: <ME3P282MB25488BE7C39BF0C35CD0DA5D8CA82@ME3P282MB2548.AUSP282.PROD.OUTLOOK.COM>
(cherry picked from commit 903cc9e1173e0778caa50871e8275c898770c690)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/chardev/char-win-stdio.c b/chardev/char-win-stdio.c
index eb830eabd9..6e59db84dd 100644
--- a/chardev/char-win-stdio.c
+++ b/chardev/char-win-stdio.c
@@ -33,6 +33,7 @@
struct WinStdioChardev {
Chardev parent;
HANDLE hStdIn;
+ DWORD dwOldMode;
HANDLE hInputReadyEvent;
HANDLE hInputDoneEvent;
HANDLE hInputThread;
@@ -159,6 +160,7 @@ static void qemu_chr_open_stdio(Chardev *chr,
}
is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0;
+ stdio->dwOldMode = dwMode;
if (is_console) {
if (qemu_add_wait_object(stdio->hStdIn,
@@ -221,6 +223,9 @@ static void char_win_stdio_finalize(Object *obj)
{
WinStdioChardev *stdio = WIN_STDIO_CHARDEV(obj);
+ if (stdio->hStdIn != INVALID_HANDLE_VALUE) {
+ SetConsoleMode(stdio->hStdIn, stdio->dwOldMode);
+ }
if (stdio->hInputReadyEvent != INVALID_HANDLE_VALUE) {
CloseHandle(stdio->hInputReadyEvent);
}
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 09/40] hw/intc/loongson_ipi: Access memory in little endian
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (7 preceding siblings ...)
2024-09-06 5:15 ` [Stable-7.2.14 08/40] chardev/char-win-stdio.c: restore old console mode Michael Tokarev
@ 2024-09-06 5:15 ` Michael Tokarev
2024-09-06 5:15 ` [Stable-7.2.14 10/40] util/async.c: Forbid negative min/max in aio_context_set_thread_pool_params() Michael Tokarev
` (30 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:15 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Bibo Mao, Philippe Mathieu-Daudé, Song Gao,
Richard Henderson, Jiaxun Yang, Michael Tokarev
From: Bibo Mao <maobibo@loongson.cn>
Loongson IPI is only available in little-endian,
so use that to access the guest memory (in case
we run on a big-endian host).
Cc: qemu-stable@nongnu.org
Signed-off-by: Bibo Mao <maobibo@loongson.cn>
Fixes: f6783e3438 ("hw/loongarch: Add LoongArch ipi interrupt support")
[PMD: Extracted from bigger commit, added commit description]
Co-Developed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Reviewed-by: Bibo Mao <maobibo@loongson.cn>
Tested-by: Bibo Mao <maobibo@loongson.cn>
Acked-by: Song Gao <gaosong@loongson.cn>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
Tested-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
Message-Id: <20240718133312.10324-3-philmd@linaro.org>
(cherry picked from commit 2465c89fb983eed670007742bd68c7d91b6d6f85)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
(Mjt: fixups for 7.2, for lack of:
v9.0.0-583-g91d0b151de4c "hw/intc/loongson_ipi: Implement IOCSR address space for MIPS"
v9.0.0-582-gb4a12dfc2132 "hw/intc/loongarch_ipi: Rename as loongson_ipi"
v8.2.0-545-gfdd6ee0b7653 "hw/intc/loongarch_ipi: Use MemTxAttrs interface for ipi ops")
diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c
index 40e98af2ce..a4079e3732 100644
--- a/hw/intc/loongarch_ipi.c
+++ b/hw/intc/loongarch_ipi.c
@@ -12,6 +12,7 @@
#include "qapi/error.h"
#include "qemu/log.h"
#include "exec/address-spaces.h"
+#include "exec/memory.h"
#include "hw/loongarch/virt.h"
#include "migration/vmstate.h"
#include "target/loongarch/internals.h"
@@ -59,8 +60,8 @@ static void send_ipi_data(CPULoongArchState *env, target_ulong val, target_ulong
* if the mask is 0, we need not to do anything.
*/
if ((val >> 27) & 0xf) {
- data = address_space_ldl(&env->address_space_iocsr, addr,
- MEMTXATTRS_UNSPECIFIED, NULL);
+ data = address_space_ldl_le(&env->address_space_iocsr, addr,
+ MEMTXATTRS_UNSPECIFIED, NULL);
for (i = 0; i < 4; i++) {
/* get mask for byte writing */
if (val & (0x1 << (27 + i))) {
@@ -71,8 +72,8 @@ static void send_ipi_data(CPULoongArchState *env, target_ulong val, target_ulong
data &= mask;
data |= (val >> 32) & ~mask;
- address_space_stl(&env->address_space_iocsr, addr,
- data, MEMTXATTRS_UNSPECIFIED, NULL);
+ address_space_stl_le(&env->address_space_iocsr, addr,
+ data, MEMTXATTRS_UNSPECIFIED, NULL);
}
static void ipi_send(uint64_t val)
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 10/40] util/async.c: Forbid negative min/max in aio_context_set_thread_pool_params()
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (8 preceding siblings ...)
2024-09-06 5:15 ` [Stable-7.2.14 09/40] hw/intc/loongson_ipi: Access memory in little endian Michael Tokarev
@ 2024-09-06 5:15 ` Michael Tokarev
2024-09-06 5:15 ` [Stable-7.2.14 11/40] hw/virtio: Fix the de-initialization of vhost-user devices Michael Tokarev
` (29 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:15 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Peter Maydell, Philippe Mathieu-Daudé,
Stefan Hajnoczi, Michael Tokarev
From: Peter Maydell <peter.maydell@linaro.org>
aio_context_set_thread_pool_params() takes two int64_t arguments to
set the minimum and maximum number of threads in the pool. We do
some bounds checking on these, but we don't catch the case where the
inputs are negative. This means that later in the function when we
assign these inputs to the AioContext::thread_pool_min and
::thread_pool_max fields, which are of type int, the values might
overflow the smaller type.
A negative number of threads is meaningless, so make
aio_context_set_thread_pool_params() return an error if either min or
max are negative.
Resolves: Coverity CID 1547605
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Message-id: 20240723150927.1396456-1-peter.maydell@linaro.org
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 851495571d14fe2226c52b9d423f88a4f5460836)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/util/async.c b/util/async.c
index a1f07fc5a7..0cc3037e0c 100644
--- a/util/async.c
+++ b/util/async.c
@@ -744,7 +744,7 @@ void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min,
int64_t max, Error **errp)
{
- if (min > max || !max || min > INT_MAX || max > INT_MAX) {
+ if (min > max || max <= 0 || min < 0 || min > INT_MAX || max > INT_MAX) {
error_setg(errp, "bad thread-pool-min/thread-pool-max values");
return;
}
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 11/40] hw/virtio: Fix the de-initialization of vhost-user devices
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (9 preceding siblings ...)
2024-09-06 5:15 ` [Stable-7.2.14 10/40] util/async.c: Forbid negative min/max in aio_context_set_thread_pool_params() Michael Tokarev
@ 2024-09-06 5:15 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 12/40] target/rx: Use target_ulong for address in LI Michael Tokarev
` (28 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:15 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Thomas Huth, Manos Pitsidianakis,
Michael S . Tsirkin, Michael Tokarev
From: Thomas Huth <thuth@redhat.com>
The unrealize functions of the various vhost-user devices are
calling the corresponding vhost_*_set_status() functions with a
status of 0 to shut down the device correctly.
Now these vhost_*_set_status() functions all follow this scheme:
bool should_start = virtio_device_should_start(vdev, status);
if (vhost_dev_is_started(&vvc->vhost_dev) == should_start) {
return;
}
if (should_start) {
/* ... do the initialization stuff ... */
} else {
/* ... do the cleanup stuff ... */
}
The problem here is virtio_device_should_start(vdev, 0) currently
always returns "true" since it internally only looks at vdev->started
instead of looking at the "status" parameter. Thus once the device
got started once, virtio_device_should_start() always returns true
and thus the vhost_*_set_status() functions return early, without
ever doing any clean-up when being called with status == 0. This
causes e.g. problems when trying to hot-plug and hot-unplug a vhost
user devices multiple times since the de-initialization step is
completely skipped during the unplug operation.
This bug has been introduced in commit 9f6bcfd99f ("hw/virtio: move
vm_running check to virtio_device_started") which replaced
should_start = status & VIRTIO_CONFIG_S_DRIVER_OK;
with
should_start = virtio_device_started(vdev, status);
which later got replaced by virtio_device_should_start(). This blocked
the possibility to set should_start to false in case the status flag
VIRTIO_CONFIG_S_DRIVER_OK was not set.
Fix it by adjusting the virtio_device_should_start() function to
only consider the status flag instead of vdev->started. Since this
function is only used in the various vhost_*_set_status() functions
for exactly the same purpose, it should be fine to fix it in this
central place there without any risk to change the behavior of other
code.
Fixes: 9f6bcfd99f ("hw/virtio: move vm_running check to virtio_device_started")
Buglink: https://issues.redhat.com/browse/RHEL-40708
Signed-off-by: Thomas Huth <thuth@redhat.com>
Message-Id: <20240618121958.88673-1-thuth@redhat.com>
Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
(cherry picked from commit d72479b11797c28893e1e3fc565497a9cae5ca16)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
index c1a7c9bd3b..aa7f7d3dd6 100644
--- a/include/hw/virtio/virtio.h
+++ b/include/hw/virtio/virtio.h
@@ -425,9 +425,9 @@ static inline bool virtio_device_started(VirtIODevice *vdev, uint8_t status)
* @vdev - the VirtIO device
* @status - the devices status bits
*
- * This is similar to virtio_device_started() but also encapsulates a
- * check on the VM status which would prevent a device starting
- * anyway.
+ * This is similar to virtio_device_started() but ignores vdev->started
+ * and also encapsulates a check on the VM status which would prevent a
+ * device from starting anyway.
*/
static inline bool virtio_device_should_start(VirtIODevice *vdev, uint8_t status)
{
@@ -435,7 +435,7 @@ static inline bool virtio_device_should_start(VirtIODevice *vdev, uint8_t status
return false;
}
- return virtio_device_started(vdev, status);
+ return status & VIRTIO_CONFIG_S_DRIVER_OK;
}
static inline void virtio_set_started(VirtIODevice *vdev, bool started)
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 12/40] target/rx: Use target_ulong for address in LI
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (10 preceding siblings ...)
2024-09-06 5:15 ` [Stable-7.2.14 11/40] hw/virtio: Fix the de-initialization of vhost-user devices Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 13/40] hw/char/bcm2835_aux: Fix assert when receive FIFO fills up Michael Tokarev
` (27 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Richard Henderson, Thomas Huth, Michael Tokarev
From: Richard Henderson <richard.henderson@linaro.org>
Using int32_t meant that the address was sign-extended to uint64_t
when passing to translator_ld*, triggering an assert.
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2453
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Tested-by: Thomas Huth <thuth@redhat.com>
(cherry picked from commit 83340193b991e7a974f117baa86a04db1fd835a9)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/target/rx/translate.c b/target/rx/translate.c
index 87a3f54adb..4233622c4e 100644
--- a/target/rx/translate.c
+++ b/target/rx/translate.c
@@ -83,7 +83,8 @@ static uint32_t decode_load_bytes(DisasContext *ctx, uint32_t insn,
static uint32_t li(DisasContext *ctx, int sz)
{
- int32_t tmp, addr;
+ target_ulong addr;
+ uint32_t tmp;
CPURXState *env = ctx->env;
addr = ctx->base.pc_next;
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 13/40] hw/char/bcm2835_aux: Fix assert when receive FIFO fills up
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (11 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 12/40] target/rx: Use target_ulong for address in LI Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 14/40] hw/misc/bcm2835_property: Fix handling of FRAMEBUFFER_SET_PALETTE Michael Tokarev
` (26 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Frederik van Hövell, Cryptjar,
Philippe Mathieu-Daudé, Peter Maydell, Michael Tokarev
From: Frederik van Hövell <frederik@fvhovell.nl>
When a bare-metal application on the raspi3 board reads the
AUX_MU_STAT_REG MMIO register while the device's buffer is
at full receive FIFO capacity
(i.e. `s->read_count == BCM2835_AUX_RX_FIFO_LEN`) the
assertion `assert(s->read_count < BCM2835_AUX_RX_FIFO_LEN)`
fails.
Reported-by: Cryptjar <cryptjar@junk.studio>
Suggested-by: Cryptjar <cryptjar@junk.studio>
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/459
Signed-off-by: Frederik van Hövell <frederik@fvhovell.nl>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
[PMM: commit message tweaks]
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit 546d574b11e02bfd5b15cdf1564842c14516dfab)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c
index 96410b1ff8..0f1b28547e 100644
--- a/hw/char/bcm2835_aux.c
+++ b/hw/char/bcm2835_aux.c
@@ -138,7 +138,7 @@ static uint64_t bcm2835_aux_read(void *opaque, hwaddr offset, unsigned size)
res = 0x30e; /* space in the output buffer, empty tx fifo, idle tx/rx */
if (s->read_count > 0) {
res |= 0x1; /* data in input buffer */
- assert(s->read_count < BCM2835_AUX_RX_FIFO_LEN);
+ assert(s->read_count <= BCM2835_AUX_RX_FIFO_LEN);
res |= ((uint32_t)s->read_count) << 16; /* rx fifo fill level */
}
return res;
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 14/40] hw/misc/bcm2835_property: Fix handling of FRAMEBUFFER_SET_PALETTE
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (12 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 13/40] hw/char/bcm2835_aux: Fix assert when receive FIFO fills up Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 15/40] target/arm: Don't assert for 128-bit tile accesses when SVL is 128 Michael Tokarev
` (25 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Peter Maydell, Philippe Mathieu-Daudé,
Michael Tokarev
From: Peter Maydell <peter.maydell@linaro.org>
The documentation of the "Set palette" mailbox property at
https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface#set-palette
says it has the form:
Length: 24..1032
Value:
u32: offset: first palette index to set (0-255)
u32: length: number of palette entries to set (1-256)
u32...: RGBA palette values (offset to offset+length-1)
We get this wrong in a couple of ways:
* we aren't checking the offset and length are in range, so the guest
can make us spin for a long time by providing a large length
* the bounds check on our loop is wrong: we should iterate through
'length' palette entries, not 'length - offset' entries
Fix the loop to implement the bounds checks and get the loop
condition right. In the process, make the variables local to
this switch case, rather than function-global, so it's clearer
what type they are when reading the code.
Cc: qemu-stable@nongnu.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Message-id: 20240723131029.1159908-2-peter.maydell@linaro.org
(cherry picked from commit 0892fffc2abaadfb5d8b79bb0250ae1794862560)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
(Mjt: context fix due to lack of
v9.0.0-1812-g5d5f1b60916a "hw/misc: Implement mailbox properties for customer OTP and device specific private keys"
v8.0.0-1924-g251918266666 "hw/misc/bcm2835_property: Use 'raspberrypi-fw-defs.h' definitions"
also remove now-unused local `n' variable which gets removed in the next change in this file,
v9.0.0-2720-g32f1c201eedf "hw/misc/bcm2835_property: Avoid overflow in OTP access properties")
diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c
index de056ea2df..c7834d3fc7 100644
--- a/hw/misc/bcm2835_property.c
+++ b/hw/misc/bcm2835_property.c
@@ -26,8 +26,6 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
uint32_t tot_len;
size_t resplen;
uint32_t tmp;
- int n;
- uint32_t offset, length, color;
/*
* Copy the current state of the framebuffer config; we will update
@@ -258,18 +256,25 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
resplen = 16;
break;
case 0x0004800b: /* Set palette */
- offset = ldl_le_phys(&s->dma_as, value + 12);
- length = ldl_le_phys(&s->dma_as, value + 16);
- n = 0;
- while (n < length - offset) {
- color = ldl_le_phys(&s->dma_as, value + 20 + (n << 2));
- stl_le_phys(&s->dma_as,
- s->fbdev->vcram_base + ((offset + n) << 2), color);
- n++;
+ {
+ uint32_t offset = ldl_le_phys(&s->dma_as, value + 12);
+ uint32_t length = ldl_le_phys(&s->dma_as, value + 16);
+ int resp;
+
+ if (offset > 255 || length < 1 || length > 256) {
+ resp = 1; /* invalid request */
+ } else {
+ for (uint32_t e = 0; e < length; e++) {
+ uint32_t color = ldl_le_phys(&s->dma_as, value + 20 + (e << 2));
+ stl_le_phys(&s->dma_as,
+ s->fbdev->vcram_base + ((offset + e) << 2), color);
+ }
+ resp = 0;
}
- stl_le_phys(&s->dma_as, value + 12, 0);
+ stl_le_phys(&s->dma_as, value + 12, resp);
resplen = 4;
break;
+ }
case 0x00040013: /* Get number of displays */
stl_le_phys(&s->dma_as, value + 12, 1);
resplen = 4;
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 15/40] target/arm: Don't assert for 128-bit tile accesses when SVL is 128
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (13 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 14/40] hw/misc/bcm2835_property: Fix handling of FRAMEBUFFER_SET_PALETTE Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 16/40] target/arm: Fix UMOPA/UMOPS of 16-bit values Michael Tokarev
` (24 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Peter Maydell, Richard Henderson, Michael Tokarev
From: Peter Maydell <peter.maydell@linaro.org>
For an instruction which accesses a 128-bit element tile when
the SVL is also 128 (for example MOV z0.Q, p0/M, ZA0H.Q[w0,0]),
we will assert in get_tile_rowcol():
qemu-system-aarch64: ../../tcg/tcg-op.c:926: tcg_gen_deposit_z_i32: Assertion `len > 0' failed.
This happens because we calculate
len = ctz32(streaming_vec_reg_size(s)) - esz;$
but if the SVL and the element size are the same len is 0, and
the deposit operation asserts.
In this case the ZA storage contains exactly one 128 bit
element ZA tile, and the horizontal or vertical slice is just
that tile. This means that regardless of the index value in
the Ws register, we always access that tile. (In pseudocode terms,
we calculate (index + offset) MOD 1, which is 0.)
Special case the len == 0 case to avoid hitting the assertion
in tcg_gen_deposit_z_i32().
Cc: qemu-stable@nongnu.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20240722172957.1041231-2-peter.maydell@linaro.org
(cherry picked from commit 56f1c0db928aae0b83fd91c89ddb226b137e2b21)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/target/arm/translate-sme.c b/target/arm/translate-sme.c
index 8cce34e117..0fcd4ad950 100644
--- a/target/arm/translate-sme.c
+++ b/target/arm/translate-sme.c
@@ -56,7 +56,15 @@ static TCGv_ptr get_tile_rowcol(DisasContext *s, int esz, int rs,
/* Prepare a power-of-two modulo via extraction of @len bits. */
len = ctz32(streaming_vec_reg_size(s)) - esz;
- if (vertical) {
+ if (!len) {
+ /*
+ * SVL is 128 and the element size is 128. There is exactly
+ * one 128x128 tile in the ZA storage, and so we calculate
+ * (Rs + imm) MOD 1, which is always 0. We need to special case
+ * this because TCG doesn't allow deposit ops with len 0.
+ */
+ tcg_gen_movi_i32(tmp, 0);
+ } else if (vertical) {
/*
* Compute the byte offset of the index within the tile:
* (index % (svl / size)) * size
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 16/40] target/arm: Fix UMOPA/UMOPS of 16-bit values
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (14 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 15/40] target/arm: Don't assert for 128-bit tile accesses when SVL is 128 Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 17/40] target/arm: Avoid shifts by -1 in tszimm_shr() and tszimm_shl() Michael Tokarev
` (23 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Peter Maydell, Richard Henderson, Michael Tokarev
From: Peter Maydell <peter.maydell@linaro.org>
The UMOPA/UMOPS instructions are supposed to multiply unsigned 8 or
16 bit elements and accumulate the products into a 64-bit element.
In the Arm ARM pseudocode, this is done with the usual
infinite-precision signed arithmetic. However our implementation
doesn't quite get it right, because in the DEF_IMOP_64() macro we do:
sum += (NTYPE)(n >> 0) * (MTYPE)(m >> 0);
where NTYPE and MTYPE are uint16_t or int16_t. In the uint16_t case,
the C usual arithmetic conversions mean the values are converted to
"int" type and the multiply is done as a 32-bit multiply. This means
that if the inputs are, for example, 0xffff and 0xffff then the
result is 0xFFFE0001 as an int, which is then promoted to uint64_t
for the accumulation into sum; this promotion incorrectly sign
extends the multiply.
Avoid the incorrect sign extension by casting to int64_t before
the multiply, so we do the multiply as 64-bit signed arithmetic,
which is a type large enough that the multiply can never
overflow into the sign bit.
(The equivalent 8-bit operations in DEF_IMOP_32() are fine, because
the 8-bit multiplies can never overflow into the sign bit of a
32-bit integer.)
Cc: qemu-stable@nongnu.org
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2372
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20240722172957.1041231-3-peter.maydell@linaro.org
(cherry picked from commit ea3f5a90f036734522e9af3bffd77e69e9f47355)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/target/arm/sme_helper.c b/target/arm/sme_helper.c
index e55bc51d69..f12f3288fd 100644
--- a/target/arm/sme_helper.c
+++ b/target/arm/sme_helper.c
@@ -1167,10 +1167,10 @@ static uint64_t NAME(uint64_t n, uint64_t m, uint64_t a, uint8_t p, bool neg) \
uint64_t sum = 0; \
/* Apply P to N as a mask, making the inactive elements 0. */ \
n &= expand_pred_h(p); \
- sum += (NTYPE)(n >> 0) * (MTYPE)(m >> 0); \
- sum += (NTYPE)(n >> 16) * (MTYPE)(m >> 16); \
- sum += (NTYPE)(n >> 32) * (MTYPE)(m >> 32); \
- sum += (NTYPE)(n >> 48) * (MTYPE)(m >> 48); \
+ sum += (int64_t)(NTYPE)(n >> 0) * (MTYPE)(m >> 0); \
+ sum += (int64_t)(NTYPE)(n >> 16) * (MTYPE)(m >> 16); \
+ sum += (int64_t)(NTYPE)(n >> 32) * (MTYPE)(m >> 32); \
+ sum += (int64_t)(NTYPE)(n >> 48) * (MTYPE)(m >> 48); \
return neg ? a - sum : a + sum; \
}
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 17/40] target/arm: Avoid shifts by -1 in tszimm_shr() and tszimm_shl()
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (15 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 16/40] target/arm: Fix UMOPA/UMOPS of 16-bit values Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 18/40] target/arm: Ignore SMCR_EL2.LEN and SVCR_EL2.LEN if EL2 is not enabled Michael Tokarev
` (22 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Peter Maydell, Richard Henderson, Michael Tokarev
From: Peter Maydell <peter.maydell@linaro.org>
The function tszimm_esz() returns a shift amount, or possibly -1 in
certain cases that correspond to unallocated encodings in the
instruction set. We catch these later in the trans_ functions
(generally with an "a-esz < 0" check), but before we do the
decodetree-generated code will also call tszimm_shr() or tszimm_sl(),
which will use the tszimm_esz() return value as a shift count without
checking that it is not negative, which is undefined behaviour.
Avoid the UB by checking the return value in tszimm_shr() and
tszimm_shl().
Cc: qemu-stable@nongnu.org
Resolves: Coverity CID 1547617, 1547694
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20240722172957.1041231-4-peter.maydell@linaro.org
(cherry picked from commit 76916dfa89e8900639c1055c07a295c06628a0bc)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c
index 7388e1dbc7..034e816491 100644
--- a/target/arm/translate-sve.c
+++ b/target/arm/translate-sve.c
@@ -61,13 +61,27 @@ static int tszimm_esz(DisasContext *s, int x)
static int tszimm_shr(DisasContext *s, int x)
{
- return (16 << tszimm_esz(s, x)) - x;
+ /*
+ * We won't use the tszimm_shr() value if tszimm_esz() returns -1 (the
+ * trans function will check for esz < 0), so we can return any
+ * value we like from here in that case as long as we avoid UB.
+ */
+ int esz = tszimm_esz(s, x);
+ if (esz < 0) {
+ return esz;
+ }
+ return (16 << esz) - x;
}
/* See e.g. LSL (immediate, predicated). */
static int tszimm_shl(DisasContext *s, int x)
{
- return x - (8 << tszimm_esz(s, x));
+ /* As with tszimm_shr(), value will be unused if esz < 0 */
+ int esz = tszimm_esz(s, x);
+ if (esz < 0) {
+ return esz;
+ }
+ return x - (8 << esz);
}
/* The SH bit is in bit 8. Extract the low 8 and shift. */
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 18/40] target/arm: Ignore SMCR_EL2.LEN and SVCR_EL2.LEN if EL2 is not enabled
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (16 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 17/40] target/arm: Avoid shifts by -1 in tszimm_shr() and tszimm_shl() Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 19/40] docs/sphinx/depfile.py: Handle env.doc2path() returning a Path not a str Michael Tokarev
` (21 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Peter Maydell, Richard Henderson, Michael Tokarev
From: Peter Maydell <peter.maydell@linaro.org>
When determining the current vector length, the SMCR_EL2.LEN and
SVCR_EL2.LEN settings should only be considered if EL2 is enabled
(compare the pseudocode CurrentSVL and CurrentNSVL which call
EL2Enabled()).
We were checking against ARM_FEATURE_EL2 rather than calling
arm_is_el2_enabled(), which meant that we would look at
SMCR_EL2/SVCR_EL2 when in Secure EL1 or Secure EL0 even if Secure EL2
was not enabled.
Use the correct check in sve_vqm1_for_el_sm().
Cc: qemu-stable@nongnu.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20240722172957.1041231-5-peter.maydell@linaro.org
(cherry picked from commit f573ac059ed060234fcef4299fae9e500d357c33)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/target/arm/helper.c b/target/arm/helper.c
index acc0470e86..5c22626b80 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -6335,7 +6335,7 @@ uint32_t sve_vqm1_for_el_sm(CPUARMState *env, int el, bool sm)
if (el <= 1 && !el_is_in_host(env, el)) {
len = MIN(len, 0xf & (uint32_t)cr[1]);
}
- if (el <= 2 && arm_feature(env, ARM_FEATURE_EL2)) {
+ if (el <= 2 && arm_is_el2_enabled(env)) {
len = MIN(len, 0xf & (uint32_t)cr[2]);
}
if (arm_feature(env, ARM_FEATURE_EL3)) {
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 19/40] docs/sphinx/depfile.py: Handle env.doc2path() returning a Path not a str
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (17 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 18/40] target/arm: Ignore SMCR_EL2.LEN and SVCR_EL2.LEN if EL2 is not enabled Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 20/40] hw/i386/amd_iommu: Don't leak memory in amdvi_update_iotlb() Michael Tokarev
` (20 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Peter Maydell, Philippe Mathieu-Daudé,
Michael Tokarev
From: Peter Maydell <peter.maydell@linaro.org>
In newer versions of Sphinx the env.doc2path() API is going to change
to return a Path object rather than a str. This was originally visible
in Sphinx 8.0.0rc1, but has been rolled back for the final 8.0.0
release. However it will probably emit a deprecation warning and is
likely to change for good in 9.0:
https://github.com/sphinx-doc/sphinx/issues/12686
Our use in depfile.py assumes a str, and if it is passed a Path
it will fall over:
Handler <function write_depfile at 0x77a1775ff560> for event 'build-finished' threw an exception (exception: unsupported operand type(s) for +: 'PosixPath' and 'str')
Wrapping the env.doc2path() call in str() will coerce a Path object
to the str we expect, and have no effect in older Sphinx versions
that do return a str.
Cc: qemu-stable@nongnu.org
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2458
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Message-ID: <20240729120533.2486427-1-peter.maydell@linaro.org>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
(cherry picked from commit 48e5b5f994bccf161dd88a67fdd819d4bfb400f1)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/docs/sphinx/depfile.py b/docs/sphinx/depfile.py
index afdcbcec6e..e74be6af98 100644
--- a/docs/sphinx/depfile.py
+++ b/docs/sphinx/depfile.py
@@ -19,7 +19,7 @@
def get_infiles(env):
for x in env.found_docs:
- yield env.doc2path(x)
+ yield str(env.doc2path(x))
yield from ((os.path.join(env.srcdir, dep)
for dep in env.dependencies[x]))
for mod in sys.modules.values():
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 20/40] hw/i386/amd_iommu: Don't leak memory in amdvi_update_iotlb()
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (18 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 19/40] docs/sphinx/depfile.py: Handle env.doc2path() returning a Path not a str Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 21/40] hw/arm/mps2-tz.c: fix RX/TX interrupts order Michael Tokarev
` (19 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Peter Maydell, Michael S . Tsirkin, Michael Tokarev
From: Peter Maydell <peter.maydell@linaro.org>
In amdvi_update_iotlb() we will only put a new entry in the hash
table if to_cache.perm is not IOMMU_NONE. However we allocate the
memory for the new AMDVIIOTLBEntry and for the hash table key
regardless. This means that in the IOMMU_NONE case we will leak the
memory we alloacted.
Move the allocations into the if() to the point where we know we're
going to add the item to the hash table.
Cc: qemu-stable@nongnu.org
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2452
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Message-Id: <20240731170019.3590563-1-peter.maydell@linaro.org>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
(cherry picked from commit 9a45b0761628cc59267b3283a85d15294464ac31)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c
index a20f3e1d50..02597db1e1 100644
--- a/hw/i386/amd_iommu.c
+++ b/hw/i386/amd_iommu.c
@@ -346,12 +346,12 @@ static void amdvi_update_iotlb(AMDVIState *s, uint16_t devid,
uint64_t gpa, IOMMUTLBEntry to_cache,
uint16_t domid)
{
- AMDVIIOTLBEntry *entry = g_new(AMDVIIOTLBEntry, 1);
- uint64_t *key = g_new(uint64_t, 1);
- uint64_t gfn = gpa >> AMDVI_PAGE_SHIFT_4K;
-
/* don't cache erroneous translations */
if (to_cache.perm != IOMMU_NONE) {
+ AMDVIIOTLBEntry *entry = g_new(AMDVIIOTLBEntry, 1);
+ uint64_t *key = g_new(uint64_t, 1);
+ uint64_t gfn = gpa >> AMDVI_PAGE_SHIFT_4K;
+
trace_amdvi_cache_update(domid, PCI_BUS_NUM(devid), PCI_SLOT(devid),
PCI_FUNC(devid), gpa, to_cache.translated_addr);
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 21/40] hw/arm/mps2-tz.c: fix RX/TX interrupts order
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (19 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 20/40] hw/i386/amd_iommu: Don't leak memory in amdvi_update_iotlb() Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 22/40] target/arm: Handle denormals correctly for FMOPA (widening) Michael Tokarev
` (18 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Marco Palumbi, Peter Maydell, Michael Tokarev
From: Marco Palumbi <Marco.Palumbi@tii.ae>
The order of the RX and TX interrupts are swapped.
This commit fixes the order as per the following documents:
* https://developer.arm.com/documentation/dai0505/latest/
* https://developer.arm.com/documentation/dai0521/latest/
* https://developer.arm.com/documentation/dai0524/latest/
* https://developer.arm.com/documentation/dai0547/latest/
Cc: qemu-stable@nongnu.org
Signed-off-by: Marco Palumbi <Marco.Palumbi@tii.ae>
Message-id: 20240730073123.72992-1-marco@palumbi.it
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit 5a558be93ad628e5bed6e0ee062870f49251725c)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c
index 284c09c91d..334cd836c3 100644
--- a/hw/arm/mps2-tz.c
+++ b/hw/arm/mps2-tz.c
@@ -427,7 +427,7 @@ static MemoryRegion *make_uart(MPS2TZMachineState *mms, void *opaque,
const char *name, hwaddr size,
const int *irqs, const PPCExtraData *extradata)
{
- /* The irq[] array is tx, rx, combined, in that order */
+ /* The irq[] array is rx, tx, combined, in that order */
MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_GET_CLASS(mms);
CMSDKAPBUART *uart = opaque;
int i = uart - &mms->uart[0];
@@ -439,8 +439,8 @@ static MemoryRegion *make_uart(MPS2TZMachineState *mms, void *opaque,
qdev_prop_set_uint32(DEVICE(uart), "pclk-frq", mmc->apb_periph_frq);
sysbus_realize(SYS_BUS_DEVICE(uart), &error_fatal);
s = SYS_BUS_DEVICE(uart);
- sysbus_connect_irq(s, 0, get_sse_irq_in(mms, irqs[0]));
- sysbus_connect_irq(s, 1, get_sse_irq_in(mms, irqs[1]));
+ sysbus_connect_irq(s, 0, get_sse_irq_in(mms, irqs[1]));
+ sysbus_connect_irq(s, 1, get_sse_irq_in(mms, irqs[0]));
sysbus_connect_irq(s, 2, qdev_get_gpio_in(orgate_dev, i * 2));
sysbus_connect_irq(s, 3, qdev_get_gpio_in(orgate_dev, i * 2 + 1));
sysbus_connect_irq(s, 4, get_sse_irq_in(mms, irqs[2]));
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 22/40] target/arm: Handle denormals correctly for FMOPA (widening)
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (20 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 21/40] hw/arm/mps2-tz.c: fix RX/TX interrupts order Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 23/40] virtio-net: Ensure queue index fits with RSS Michael Tokarev
` (17 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Peter Maydell, Richard Henderson, Michael Tokarev
From: Peter Maydell <peter.maydell@linaro.org>
The FMOPA (widening) SME instruction takes pairs of half-precision
floating point values, widens them to single-precision, does a
two-way dot product and accumulates the results into a
single-precision destination. We don't quite correctly handle the
FPCR bits FZ and FZ16 which control flushing of denormal inputs and
outputs. This is because at the moment we pass a single float_status
value to the helper function, which then uses that configuration for
all the fp operations it does. However, because the inputs to this
operation are float16 and the outputs are float32 we need to use the
fp_status_f16 for the float16 input widening but the normal fp_status
for everything else. Otherwise we will apply the flushing control
FPCR.FZ16 to the 32-bit output rather than the FPCR.FZ control, and
incorrectly flush a denormal output to zero when we should not (or
vice-versa).
(In commit 207d30b5fdb5b we tried to fix the FZ handling but
didn't get it right, switching from "use FPCR.FZ for everything" to
"use FPCR.FZ16 for everything".)
(Mjt: it is commit d5373d7bdbee in stable-7.2)
Pass the CPU env to the sme_fmopa_h helper instead of an fp_status
pointer, and have the helper pass an extra fp_status into the
f16_dotadd() function so that we can use the right status for the
right parts of this operation.
Cc: qemu-stable@nongnu.org
Fixes: 207d30b5fdb5 ("target/arm: Use FPST_F16 for SME FMOPA (widening)")
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2373
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
(cherry picked from commit 55f9f4ee018c5ccea81d8c8c586756d7711ae46f)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
(Mjt: s/tcg_env/cpu_env/ due to missingv
8.1.0-1189-gad75a51e84af "tcg: Rename cpu_env to tcg_env")
diff --git a/target/arm/helper-sme.h b/target/arm/helper-sme.h
index d2d544a696..d33fbcd8fd 100644
--- a/target/arm/helper-sme.h
+++ b/target/arm/helper-sme.h
@@ -122,7 +122,7 @@ DEF_HELPER_FLAGS_5(sme_addha_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(sme_addva_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_7(sme_fmopa_h, TCG_CALL_NO_RWG,
- void, ptr, ptr, ptr, ptr, ptr, ptr, i32)
+ void, ptr, ptr, ptr, ptr, ptr, env, i32)
DEF_HELPER_FLAGS_7(sme_fmopa_s, TCG_CALL_NO_RWG,
void, ptr, ptr, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_7(sme_fmopa_d, TCG_CALL_NO_RWG,
diff --git a/target/arm/sme_helper.c b/target/arm/sme_helper.c
index f12f3288fd..98a4840970 100644
--- a/target/arm/sme_helper.c
+++ b/target/arm/sme_helper.c
@@ -1009,12 +1009,23 @@ static inline uint32_t f16mop_adj_pair(uint32_t pair, uint32_t pg, uint32_t neg)
}
static float32 f16_dotadd(float32 sum, uint32_t e1, uint32_t e2,
- float_status *s_std, float_status *s_odd)
+ float_status *s_f16, float_status *s_std,
+ float_status *s_odd)
{
- float64 e1r = float16_to_float64(e1 & 0xffff, true, s_std);
- float64 e1c = float16_to_float64(e1 >> 16, true, s_std);
- float64 e2r = float16_to_float64(e2 & 0xffff, true, s_std);
- float64 e2c = float16_to_float64(e2 >> 16, true, s_std);
+ /*
+ * We need three different float_status for different parts of this
+ * operation:
+ * - the input conversion of the float16 values must use the
+ * f16-specific float_status, so that the FPCR.FZ16 control is applied
+ * - operations on float32 including the final accumulation must use
+ * the normal float_status, so that FPCR.FZ is applied
+ * - we have pre-set-up copy of s_std which is set to round-to-odd,
+ * for the multiply (see below)
+ */
+ float64 e1r = float16_to_float64(e1 & 0xffff, true, s_f16);
+ float64 e1c = float16_to_float64(e1 >> 16, true, s_f16);
+ float64 e2r = float16_to_float64(e2 & 0xffff, true, s_f16);
+ float64 e2c = float16_to_float64(e2 >> 16, true, s_f16);
float64 t64;
float32 t32;
@@ -1036,20 +1047,23 @@ static float32 f16_dotadd(float32 sum, uint32_t e1, uint32_t e2,
}
void HELPER(sme_fmopa_h)(void *vza, void *vzn, void *vzm, void *vpn,
- void *vpm, void *vst, uint32_t desc)
+ void *vpm, CPUARMState *env, uint32_t desc)
{
intptr_t row, col, oprsz = simd_maxsz(desc);
uint32_t neg = simd_data(desc) * 0x80008000u;
uint16_t *pn = vpn, *pm = vpm;
- float_status fpst_odd, fpst_std;
+ float_status fpst_odd, fpst_std, fpst_f16;
/*
- * Make a copy of float_status because this operation does not
- * update the cumulative fp exception status. It also produces
- * default nans. Make a second copy with round-to-odd -- see above.
+ * Make copies of fp_status and fp_status_f16, because this operation
+ * does not update the cumulative fp exception status. It also
+ * produces default NaNs. We also need a second copy of fp_status with
+ * round-to-odd -- see above.
*/
- fpst_std = *(float_status *)vst;
+ fpst_f16 = env->vfp.fp_status_f16;
+ fpst_std = env->vfp.fp_status;
set_default_nan_mode(true, &fpst_std);
+ set_default_nan_mode(true, &fpst_f16);
fpst_odd = fpst_std;
set_float_rounding_mode(float_round_to_odd, &fpst_odd);
@@ -1069,7 +1083,8 @@ void HELPER(sme_fmopa_h)(void *vza, void *vzn, void *vzm, void *vpn,
uint32_t m = *(uint32_t *)(vzm + H1_4(col));
m = f16mop_adj_pair(m, pcol, 0);
- *a = f16_dotadd(*a, n, m, &fpst_std, &fpst_odd);
+ *a = f16_dotadd(*a, n, m,
+ &fpst_f16, &fpst_std, &fpst_odd);
}
col += 4;
pcol >>= 4;
diff --git a/target/arm/translate-sme.c b/target/arm/translate-sme.c
index 0fcd4ad950..c864bd016c 100644
--- a/target/arm/translate-sme.c
+++ b/target/arm/translate-sme.c
@@ -376,8 +376,29 @@ static bool do_outprod_fpst(DisasContext *s, arg_op *a, MemOp esz,
return true;
}
-TRANS_FEAT(FMOPA_h, aa64_sme, do_outprod_fpst, a,
- MO_32, FPST_FPCR_F16, gen_helper_sme_fmopa_h)
+static bool do_outprod_env(DisasContext *s, arg_op *a, MemOp esz,
+ gen_helper_gvec_5_ptr *fn)
+{
+ int svl = streaming_vec_reg_size(s);
+ uint32_t desc = simd_desc(svl, svl, a->sub);
+ TCGv_ptr za, zn, zm, pn, pm;
+
+ if (!sme_smza_enabled_check(s)) {
+ return true;
+ }
+
+ za = get_tile(s, esz, a->zad);
+ zn = vec_full_reg_ptr(s, a->zn);
+ zm = vec_full_reg_ptr(s, a->zm);
+ pn = pred_full_reg_ptr(s, a->pn);
+ pm = pred_full_reg_ptr(s, a->pm);
+
+ fn(za, zn, zm, pn, pm, cpu_env, tcg_constant_i32(desc));
+ return true;
+}
+
+TRANS_FEAT(FMOPA_h, aa64_sme, do_outprod_env, a,
+ MO_32, gen_helper_sme_fmopa_h)
TRANS_FEAT(FMOPA_s, aa64_sme, do_outprod_fpst, a,
MO_32, FPST_FPCR, gen_helper_sme_fmopa_s)
TRANS_FEAT(FMOPA_d, aa64_sme_f64f64, do_outprod_fpst, a,
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 23/40] virtio-net: Ensure queue index fits with RSS
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (21 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 22/40] target/arm: Handle denormals correctly for FMOPA (widening) Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 24/40] virtio-net: Fix network stall at the host side waiting for kick Michael Tokarev
` (16 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Akihiko Odaki, Zhibin Hu, Michael S . Tsirkin,
Jason Wang, Michael Tokarev
From: Akihiko Odaki <akihiko.odaki@daynix.com>
Ensure the queue index points to a valid queue when software RSS
enabled. The new calculation matches with the behavior of Linux's TAP
device with the RSS eBPF program.
Fixes: 4474e37a5b3a ("virtio-net: implement RX RSS processing")
Reported-by: Zhibin Hu <huzhibin5@huawei.com>
Cc: qemu-stable@nongnu.org
Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Jason Wang <jasowang@redhat.com>
(cherry picked from commit f1595ceb9aad36a6c1da95bcb77ab9509b38822d)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
Fixes: CVE-2024-6505
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index beadea5bf8..ebee5db1bc 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -1846,7 +1846,8 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf,
if (!no_rss && n->rss_data.enabled && n->rss_data.enabled_software_rss) {
int index = virtio_net_process_rss(nc, buf, size);
if (index >= 0) {
- NetClientState *nc2 = qemu_get_subqueue(n->nic, index);
+ NetClientState *nc2 =
+ qemu_get_subqueue(n->nic, index % n->curr_queue_pairs);
return virtio_net_receive_rcu(nc2, buf, size, true);
}
}
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 24/40] virtio-net: Fix network stall at the host side waiting for kick
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (22 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 23/40] virtio-net: Ensure queue index fits with RSS Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 25/40] hw/sd/sdhci: Reset @data_count index on invalid ADMA transfers Michael Tokarev
` (15 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, thomas, Michael S . Tsirkin, Jason Wang,
Michael Tokarev
From: thomas <east.moutain.yang@gmail.com>
Patch 06b12970174 ("virtio-net: fix network stall under load")
added double-check to test whether the available buffer size
can satisfy the request or not, in case the guest has added
some buffers to the avail ring simultaneously after the first
check. It will be lucky if the available buffer size becomes
okay after the double-check, then the host can send the packet
to the guest. If the buffer size still can't satisfy the request,
even if the guest has added some buffers, viritio-net would
stall at the host side forever.
The patch enables notification and checks whether the guest has
added some buffers since last check of available buffers when
the available buffers are insufficient. If no buffer is added,
return false, else recheck the available buffers in the loop.
If the available buffers are sufficient, disable notification
and return true.
Changes:
1. Change the return type of virtqueue_get_avail_bytes() from void
to int, it returns an opaque that represents the shadow_avail_idx
of the virtqueue on success, else -1 on error.
2. Add a new API: virtio_queue_enable_notification_and_check(),
it takes an opaque as input arg which is returned from
virtqueue_get_avail_bytes(). It enables notification firstly,
then checks whether the guest has added some buffers since
last check of available buffers or not by virtio_queue_poll(),
return ture if yes.
The patch also reverts patch "06b12970174".
The case below can reproduce the stall.
Guest 0
+--------+
| iperf |
---------------> | server |
Host | +--------+
+--------+ | ...
| iperf |----
| client |---- Guest n
+--------+ | +--------+
| | iperf |
---------------> | server |
+--------+
Boot many guests from qemu with virtio network:
qemu ... -netdev tap,id=net_x \
-device virtio-net-pci-non-transitional,\
iommu_platform=on,mac=xx:xx:xx:xx:xx:xx,netdev=net_x
Each guest acts as iperf server with commands below:
iperf3 -s -D -i 10 -p 8001
iperf3 -s -D -i 10 -p 8002
The host as iperf client:
iperf3 -c guest_IP -p 8001 -i 30 -w 256k -P 20 -t 40000
iperf3 -c guest_IP -p 8002 -i 30 -w 256k -P 20 -t 40000
After some time, the host loses connection to the guest,
the guest can send packet to the host, but can't receive
packet from the host.
It's more likely to happen if SWIOTLB is enabled in the guest,
allocating and freeing bounce buffer takes some CPU ticks,
copying from/to bounce buffer takes more CPU ticks, compared
with that there is no bounce buffer in the guest.
Once the rate of producing packets from the host approximates
the rate of receiveing packets in the guest, the guest would
loop in NAPI.
receive packets ---
| |
v |
free buf virtnet_poll
| |
v |
add buf to avail ring ---
|
| need kick the host?
| NAPI continues
v
receive packets ---
| |
v |
free buf virtnet_poll
| |
v |
add buf to avail ring ---
|
v
... ...
On the other hand, the host fetches free buf from avail
ring, if the buf in the avail ring is not enough, the
host notifies the guest the event by writing the avail
idx read from avail ring to the event idx of used ring,
then the host goes to sleep, waiting for the kick signal
from the guest.
Once the guest finds the host is waiting for kick singal
(in virtqueue_kick_prepare_split()), it kicks the host.
The host may stall forever at the sequences below:
Host Guest
------------ -----------
fetch buf, send packet receive packet ---
... ... |
fetch buf, send packet add buf |
... add buf virtnet_poll
buf not enough avail idx-> add buf |
read avail idx add buf |
add buf ---
receive packet ---
write event idx ... |
wait for kick add buf virtnet_poll
... |
---
no more packet, exit NAPI
In the first loop of NAPI above, indicated in the range of
virtnet_poll above, the host is sending packets while the
guest is receiving packets and adding buffers.
step 1: The buf is not enough, for example, a big packet
needs 5 buf, but the available buf count is 3.
The host read current avail idx.
step 2: The guest adds some buf, then checks whether the
host is waiting for kick signal, not at this time.
The used ring is not empty, the guest continues
the second loop of NAPI.
step 3: The host writes the avail idx read from avail
ring to used ring as event idx via
virtio_queue_set_notification(q->rx_vq, 1).
step 4: At the end of the second loop of NAPI, recheck
whether kick is needed, as the event idx in the
used ring written by the host is beyound the
range of kick condition, the guest will not
send kick signal to the host.
Fixes: 06b12970174 ("virtio-net: fix network stall under load")
Cc: qemu-stable@nongnu.org
Signed-off-by: Wencheng Yang <east.moutain.yang@gmail.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Jason Wang <jasowang@redhat.com>
(cherry picked from commit f937309fbdbb48c354220a3e7110c202ae4aa7fa)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
(Mjt: context fixup in include/hw/virtio/virtio.h)
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index ebee5db1bc..925a5c319e 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -1597,24 +1597,28 @@ static bool virtio_net_can_receive(NetClientState *nc)
static int virtio_net_has_buffers(VirtIONetQueue *q, int bufsize)
{
+ int opaque;
+ unsigned int in_bytes;
VirtIONet *n = q->n;
- if (virtio_queue_empty(q->rx_vq) ||
- (n->mergeable_rx_bufs &&
- !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) {
- virtio_queue_set_notification(q->rx_vq, 1);
-
- /* To avoid a race condition where the guest has made some buffers
- * available after the above check but before notification was
- * enabled, check for available buffers again.
- */
- if (virtio_queue_empty(q->rx_vq) ||
- (n->mergeable_rx_bufs &&
- !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) {
+
+ while (virtio_queue_empty(q->rx_vq) || n->mergeable_rx_bufs) {
+ opaque = virtqueue_get_avail_bytes(q->rx_vq, &in_bytes, NULL,
+ bufsize, 0);
+ /* Buffer is enough, disable notifiaction */
+ if (bufsize <= in_bytes) {
+ break;
+ }
+
+ if (virtio_queue_enable_notification_and_check(q->rx_vq, opaque)) {
+ /* Guest has added some buffers, try again */
+ continue;
+ } else {
return 0;
}
}
virtio_queue_set_notification(q->rx_vq, 0);
+
return 1;
}
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index 1227e3d692..d0d13f4766 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -1153,6 +1153,60 @@ int virtio_queue_empty(VirtQueue *vq)
}
}
+static bool virtio_queue_split_poll(VirtQueue *vq, unsigned shadow_idx)
+{
+ if (unlikely(!vq->vring.avail)) {
+ return false;
+ }
+
+ return (uint16_t)shadow_idx != vring_avail_idx(vq);
+}
+
+static bool virtio_queue_packed_poll(VirtQueue *vq, unsigned shadow_idx)
+{
+ VRingPackedDesc desc;
+ VRingMemoryRegionCaches *caches;
+
+ if (unlikely(!vq->vring.desc)) {
+ return false;
+ }
+
+ caches = vring_get_region_caches(vq);
+ if (!caches) {
+ return false;
+ }
+
+ vring_packed_desc_read(vq->vdev, &desc, &caches->desc,
+ shadow_idx, true);
+
+ return is_desc_avail(desc.flags, vq->shadow_avail_wrap_counter);
+}
+
+static bool virtio_queue_poll(VirtQueue *vq, unsigned shadow_idx)
+{
+ if (virtio_device_disabled(vq->vdev)) {
+ return false;
+ }
+
+ if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED)) {
+ return virtio_queue_packed_poll(vq, shadow_idx);
+ } else {
+ return virtio_queue_split_poll(vq, shadow_idx);
+ }
+}
+
+bool virtio_queue_enable_notification_and_check(VirtQueue *vq,
+ int opaque)
+{
+ virtio_queue_set_notification(vq, 1);
+
+ if (opaque >= 0) {
+ return virtio_queue_poll(vq, (unsigned)opaque);
+ } else {
+ return false;
+ }
+}
+
static void virtqueue_unmap_sg(VirtQueue *vq, const VirtQueueElement *elem,
unsigned int len)
{
@@ -1727,9 +1781,9 @@ err:
goto done;
}
-void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes,
- unsigned int *out_bytes,
- unsigned max_in_bytes, unsigned max_out_bytes)
+int virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes,
+ unsigned int *out_bytes, unsigned max_in_bytes,
+ unsigned max_out_bytes)
{
uint16_t desc_size;
VRingMemoryRegionCaches *caches;
@@ -1762,7 +1816,7 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes,
caches);
}
- return;
+ return (int)vq->shadow_avail_idx;
err:
if (in_bytes) {
*in_bytes = 0;
@@ -1770,6 +1824,8 @@ err:
if (out_bytes) {
*out_bytes = 0;
}
+
+ return -1;
}
int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes,
diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
index aa7f7d3dd6..ab3eb182f4 100644
--- a/include/hw/virtio/virtio.h
+++ b/include/hw/virtio/virtio.h
@@ -237,9 +237,13 @@ void qemu_put_virtqueue_element(VirtIODevice *vdev, QEMUFile *f,
VirtQueueElement *elem);
int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes,
unsigned int out_bytes);
-void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes,
- unsigned int *out_bytes,
- unsigned max_in_bytes, unsigned max_out_bytes);
+/**
+ * Return <0 on error or an opaque >=0 to pass to
+ * virtio_queue_enable_notification_and_check on success.
+ */
+int virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes,
+ unsigned int *out_bytes, unsigned max_in_bytes,
+ unsigned max_out_bytes);
void virtio_notify_irqfd(VirtIODevice *vdev, VirtQueue *vq);
void virtio_notify(VirtIODevice *vdev, VirtQueue *vq);
@@ -266,6 +270,15 @@ int virtio_queue_ready(VirtQueue *vq);
int virtio_queue_empty(VirtQueue *vq);
+/**
+ * Enable notification and check whether guest has added some
+ * buffers since last call to virtqueue_get_avail_bytes.
+ *
+ * @opaque: value returned from virtqueue_get_avail_bytes
+ */
+bool virtio_queue_enable_notification_and_check(VirtQueue *vq,
+ int opaque);
+
/* Host binding interface. */
uint32_t virtio_config_readb(VirtIODevice *vdev, uint32_t addr);
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 25/40] hw/sd/sdhci: Reset @data_count index on invalid ADMA transfers
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (23 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 24/40] virtio-net: Fix network stall at the host side waiting for kick Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 26/40] vvfat: Fix bug in writing to middle of file Michael Tokarev
` (14 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Philippe Mathieu-Daudé, Zheyu Ma,
Richard Henderson, Michael Tokarev
From: Philippe Mathieu-Daudé <philmd@linaro.org>
We neglected to clear the @data_count index on ADMA error,
allowing to trigger assertion in sdhci_read_dataport() or
sdhci_write_dataport().
Cc: qemu-stable@nongnu.org
Fixes: d7dfca0807 ("hw/sdhci: introduce standard SD host controller")
Reported-by: Zheyu Ma <zheyuma97@gmail.com>
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2455
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-Id: <20240730092138.32443-4-philmd@linaro.org>
(cherry picked from commit ed5a159c3de48a581f46de4c8c02b4b295e6c52d)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
index abd503d168..c4a9b5956d 100644
--- a/hw/sd/sdhci.c
+++ b/hw/sd/sdhci.c
@@ -846,6 +846,7 @@ static void sdhci_do_adma(SDHCIState *s)
}
}
if (res != MEMTX_OK) {
+ s->data_count = 0;
if (s->errintstsen & SDHC_EISEN_ADMAERR) {
trace_sdhci_error("Set ADMA error flag");
s->errintsts |= SDHC_EIS_ADMAERR;
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 26/40] vvfat: Fix bug in writing to middle of file
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (24 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 25/40] hw/sd/sdhci: Reset @data_count index on invalid ADMA transfers Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 27/40] vvfat: Fix usage of `info.file.offset` Michael Tokarev
` (13 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Amjad Alsharafi, Kevin Wolf, Michael Tokarev
From: Amjad Alsharafi <amjadsharafi10@gmail.com>
Before this commit, the behavior when calling `commit_one_file` for
example with `offset=0x2000` (second cluster), what will happen is that
we won't fetch the next cluster from the fat, and instead use the first
cluster for the read operation.
This is due to off-by-one error here, where `i=0x2000 !< offset=0x2000`,
thus not fetching the next cluster.
Signed-off-by: Amjad Alsharafi <amjadsharafi10@gmail.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Tested-by: Kevin Wolf <kwolf@redhat.com>
Message-ID: <b97c1e1f1bc2f776061ae914f95d799d124fcd73.1721470238.git.amjadsharafi10@gmail.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit b881cf00c99e03bc8a3648581f97736ff275b18b)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/block/vvfat.c b/block/vvfat.c
index 723c91216e..741fdb0341 100644
--- a/block/vvfat.c
+++ b/block/vvfat.c
@@ -2522,8 +2522,9 @@ static int commit_one_file(BDRVVVFATState* s,
return -1;
}
- for (i = s->cluster_size; i < offset; i += s->cluster_size)
+ for (i = 0; i < offset; i += s->cluster_size) {
c = modified_fat_get(s, c);
+ }
fd = qemu_open_old(mapping->path, O_RDWR | O_CREAT | O_BINARY, 0666);
if (fd < 0) {
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 27/40] vvfat: Fix usage of `info.file.offset`
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (25 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 26/40] vvfat: Fix bug in writing to middle of file Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 28/40] vvfat: Fix wrong checks for cluster mappings invariant Michael Tokarev
` (12 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Amjad Alsharafi, Kevin Wolf, Michael Tokarev
From: Amjad Alsharafi <amjadsharafi10@gmail.com>
The field is marked as "the offset in the file (in clusters)", but it
was being used like this
`cluster_size*(nums)+mapping->info.file.offset`, which is incorrect.
Signed-off-by: Amjad Alsharafi <amjadsharafi10@gmail.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Message-ID: <72f19a7903886dda1aa78bcae0e17702ee939262.1721470238.git.amjadsharafi10@gmail.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 21b25a0e466a5bba0a45600bb8100ab564202ed1)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/block/vvfat.c b/block/vvfat.c
index 741fdb0341..3a83ab5af4 100644
--- a/block/vvfat.c
+++ b/block/vvfat.c
@@ -1407,7 +1407,9 @@ read_cluster_directory:
assert(s->current_fd);
- offset=s->cluster_size*(cluster_num-s->current_mapping->begin)+s->current_mapping->info.file.offset;
+ offset = s->cluster_size *
+ ((cluster_num - s->current_mapping->begin)
+ + s->current_mapping->info.file.offset);
if(lseek(s->current_fd, offset, SEEK_SET)!=offset)
return -3;
s->cluster=s->cluster_buffer;
@@ -1928,8 +1930,9 @@ static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s,
(mapping->mode & MODE_DIRECTORY) == 0) {
/* was modified in qcow */
- if (offset != mapping->info.file.offset + s->cluster_size
- * (cluster_num - mapping->begin)) {
+ if (offset != s->cluster_size
+ * ((cluster_num - mapping->begin)
+ + mapping->info.file.offset)) {
/* offset of this cluster in file chain has changed */
abort();
copy_it = 1;
@@ -2402,7 +2405,7 @@ static int commit_mappings(BDRVVVFATState* s,
(mapping->end - mapping->begin);
} else
next_mapping->info.file.offset = mapping->info.file.offset +
- mapping->end - mapping->begin;
+ (mapping->end - mapping->begin);
mapping = next_mapping;
}
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 28/40] vvfat: Fix wrong checks for cluster mappings invariant
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (26 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 27/40] vvfat: Fix usage of `info.file.offset` Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 29/40] vvfat: Fix reading files with non-continuous clusters Michael Tokarev
` (11 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Amjad Alsharafi, Kevin Wolf, Michael Tokarev
From: Amjad Alsharafi <amjadsharafi10@gmail.com>
How this `abort` was intended to check for was:
- if the `mapping->first_mapping_index` is not the same as
`first_mapping_index`, which **should** happen only in one case,
when we are handling the first mapping, in that case
`mapping->first_mapping_index == -1`, in all other cases, the other
mappings after the first should have the condition `true`.
- From above, we know that this is the first mapping, so if the offset
is not `0`, then abort, since this is an invalid state.
The issue was that `first_mapping_index` is not set if we are
checking from the middle, the variable `first_mapping_index` is
only set if we passed through the check `cluster_was_modified` with the
first mapping, and in the same function call we checked the other
mappings.
One approach is to go into the loop even if `cluster_was_modified`
is not true so that we will be able to set `first_mapping_index` for the
first mapping, but since `first_mapping_index` is only used here,
another approach is to just check manually for the
`mapping->first_mapping_index != -1` since we know that this is the
value for the only entry where `offset == 0` (i.e. first mapping).
Signed-off-by: Amjad Alsharafi <amjadsharafi10@gmail.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Message-ID: <b0fbca3ee208c565885838f6a7deeaeb23f4f9c2.1721470238.git.amjadsharafi10@gmail.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit f60a6f7e17bf2a2a0f0a08265ac9b077fce42858)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/block/vvfat.c b/block/vvfat.c
index 3a83ab5af4..1adcc6040f 100644
--- a/block/vvfat.c
+++ b/block/vvfat.c
@@ -1879,7 +1879,6 @@ static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s,
uint32_t cluster_num = begin_of_direntry(direntry);
uint32_t offset = 0;
- int first_mapping_index = -1;
mapping_t* mapping = NULL;
const char* basename2 = NULL;
@@ -1941,14 +1940,9 @@ static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s,
if (strcmp(basename, basename2))
copy_it = 1;
- first_mapping_index = array_index(&(s->mapping), mapping);
- }
-
- if (mapping->first_mapping_index != first_mapping_index
- && mapping->info.file.offset > 0) {
- abort();
- copy_it = 1;
}
+ assert(mapping->first_mapping_index == -1
+ || mapping->info.file.offset > 0);
/* need to write out? */
if (!was_modified && is_file(direntry)) {
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 29/40] vvfat: Fix reading files with non-continuous clusters
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (27 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 28/40] vvfat: Fix wrong checks for cluster mappings invariant Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 30/40] iotests: Add `vvfat` tests Michael Tokarev
` (10 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Amjad Alsharafi, Kevin Wolf, Michael Tokarev
From: Amjad Alsharafi <amjadsharafi10@gmail.com>
When reading with `read_cluster` we get the `mapping` with
`find_mapping_for_cluster` and then we call `open_file` for this
mapping.
The issue appear when its the same file, but a second cluster that is
not immediately after it, imagine clusters `500 -> 503`, this will give
us 2 mappings one has the range `500..501` and another `503..504`, both
point to the same file, but different offsets.
When we don't open the file since the path is the same, we won't assign
`s->current_mapping` and thus accessing way out of bound of the file.
From our example above, after `open_file` (that didn't open anything) we
will get the offset into the file with
`s->cluster_size*(cluster_num-s->current_mapping->begin)`, which will
give us `0x2000 * (504-500)`, which is out of bound for this mapping and
will produce some issues.
Signed-off-by: Amjad Alsharafi <amjadsharafi10@gmail.com>
Message-ID: <1f3ea115779abab62ba32c788073cdc99f9ad5dd.1721470238.git.amjadsharafi10@gmail.com>
[kwolf: Simplified the patch based on Amjad's analysis and input]
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 5eed3db336506b529b927ba221fe0d836e5b8819)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/block/vvfat.c b/block/vvfat.c
index 1adcc6040f..eb844c3134 100644
--- a/block/vvfat.c
+++ b/block/vvfat.c
@@ -1368,8 +1368,9 @@ static int open_file(BDRVVVFATState* s,mapping_t* mapping)
return -1;
vvfat_close_current_file(s);
s->current_fd = fd;
- s->current_mapping = mapping;
}
+
+ s->current_mapping = mapping;
return 0;
}
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 30/40] iotests: Add `vvfat` tests
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (28 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 29/40] vvfat: Fix reading files with non-continuous clusters Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 31/40] nbd/server: Plumb in new args to nbd_client_add() Michael Tokarev
` (9 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Amjad Alsharafi, Kevin Wolf, Michael Tokarev
From: Amjad Alsharafi <amjadsharafi10@gmail.com>
Added several tests to verify the implementation of the vvfat driver.
We needed a way to interact with it, so created a basic `fat16.py` driver
that handled writing correct sectors for us.
Added `vvfat` to the non-generic formats, as its not a normal image format.
Signed-off-by: Amjad Alsharafi <amjadsharafi10@gmail.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Tested-by: Kevin Wolf <kwolf@redhat.com>
Message-ID: <bb8149c945301aefbdf470a0924c07f69f9c087d.1721470238.git.amjadsharafi10@gmail.com>
[kwolf: Made mypy and pylint happy to unbreak 297]
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit c8f60bfb4345ea8343a53eaefe88d47b44c53f24)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check
index 75de1b4691..4da95cff2a 100755
--- a/tests/qemu-iotests/check
+++ b/tests/qemu-iotests/check
@@ -70,7 +70,7 @@ def make_argparser() -> argparse.ArgumentParser:
p.set_defaults(imgfmt='raw', imgproto='file')
format_list = ['raw', 'bochs', 'cloop', 'parallels', 'qcow', 'qcow2',
- 'qed', 'vdi', 'vpc', 'vhdx', 'vmdk', 'luks', 'dmg']
+ 'qed', 'vdi', 'vpc', 'vhdx', 'vmdk', 'luks', 'dmg', 'vvfat']
g_fmt = p.add_argument_group(
' image format options',
'The following options set the IMGFMT environment variable. '
diff --git a/tests/qemu-iotests/fat16.py b/tests/qemu-iotests/fat16.py
new file mode 100644
index 0000000000..7d2d052413
--- /dev/null
+++ b/tests/qemu-iotests/fat16.py
@@ -0,0 +1,690 @@
+# A simple FAT16 driver that is used to test the `vvfat` driver in QEMU.
+#
+# Copyright (C) 2024 Amjad Alsharafi <amjadsharafi10@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from typing import Callable, List, Optional, Protocol, Set
+import string
+
+SECTOR_SIZE = 512
+DIRENTRY_SIZE = 32
+ALLOWED_FILE_CHARS = set(
+ "!#$%&'()-@^_`{}~" + string.digits + string.ascii_uppercase
+)
+
+
+class MBR:
+ def __init__(self, data: bytes):
+ assert len(data) == 512
+ self.partition_table = []
+ for i in range(4):
+ partition = data[446 + i * 16 : 446 + (i + 1) * 16]
+ self.partition_table.append(
+ {
+ "status": partition[0],
+ "start_head": partition[1],
+ "start_sector": partition[2] & 0x3F,
+ "start_cylinder": ((partition[2] & 0xC0) << 2)
+ | partition[3],
+ "type": partition[4],
+ "end_head": partition[5],
+ "end_sector": partition[6] & 0x3F,
+ "end_cylinder": ((partition[6] & 0xC0) << 2)
+ | partition[7],
+ "start_lba": int.from_bytes(partition[8:12], "little"),
+ "size": int.from_bytes(partition[12:16], "little"),
+ }
+ )
+
+ def __str__(self):
+ return "\n".join(
+ [
+ f"{i}: {partition}"
+ for i, partition in enumerate(self.partition_table)
+ ]
+ )
+
+
+class FatBootSector:
+ # pylint: disable=too-many-instance-attributes
+ def __init__(self, data: bytes):
+ assert len(data) == 512
+ self.bytes_per_sector = int.from_bytes(data[11:13], "little")
+ self.sectors_per_cluster = data[13]
+ self.reserved_sectors = int.from_bytes(data[14:16], "little")
+ self.fat_count = data[16]
+ self.root_entries = int.from_bytes(data[17:19], "little")
+ total_sectors_16 = int.from_bytes(data[19:21], "little")
+ self.media_descriptor = data[21]
+ self.sectors_per_fat = int.from_bytes(data[22:24], "little")
+ self.sectors_per_track = int.from_bytes(data[24:26], "little")
+ self.heads = int.from_bytes(data[26:28], "little")
+ self.hidden_sectors = int.from_bytes(data[28:32], "little")
+ total_sectors_32 = int.from_bytes(data[32:36], "little")
+ assert (
+ total_sectors_16 == 0 or total_sectors_32 == 0
+ ), "Both total sectors (16 and 32) fields are non-zero"
+ self.total_sectors = total_sectors_16 or total_sectors_32
+ self.drive_number = data[36]
+ self.volume_id = int.from_bytes(data[39:43], "little")
+ self.volume_label = data[43:54].decode("ascii").strip()
+ self.fs_type = data[54:62].decode("ascii").strip()
+
+ def root_dir_start(self):
+ """
+ Calculate the start sector of the root directory.
+ """
+ return self.reserved_sectors + self.fat_count * self.sectors_per_fat
+
+ def root_dir_size(self):
+ """
+ Calculate the size of the root directory in sectors.
+ """
+ return (
+ self.root_entries * DIRENTRY_SIZE + self.bytes_per_sector - 1
+ ) // self.bytes_per_sector
+
+ def data_sector_start(self):
+ """
+ Calculate the start sector of the data region.
+ """
+ return self.root_dir_start() + self.root_dir_size()
+
+ def first_sector_of_cluster(self, cluster: int) -> int:
+ """
+ Calculate the first sector of the given cluster.
+ """
+ return (
+ self.data_sector_start() + (cluster - 2) * self.sectors_per_cluster
+ )
+
+ def cluster_bytes(self):
+ """
+ Calculate the number of bytes in a cluster.
+ """
+ return self.bytes_per_sector * self.sectors_per_cluster
+
+ def __str__(self):
+ return (
+ f"Bytes per sector: {self.bytes_per_sector}\n"
+ f"Sectors per cluster: {self.sectors_per_cluster}\n"
+ f"Reserved sectors: {self.reserved_sectors}\n"
+ f"FAT count: {self.fat_count}\n"
+ f"Root entries: {self.root_entries}\n"
+ f"Total sectors: {self.total_sectors}\n"
+ f"Media descriptor: {self.media_descriptor}\n"
+ f"Sectors per FAT: {self.sectors_per_fat}\n"
+ f"Sectors per track: {self.sectors_per_track}\n"
+ f"Heads: {self.heads}\n"
+ f"Hidden sectors: {self.hidden_sectors}\n"
+ f"Drive number: {self.drive_number}\n"
+ f"Volume ID: {self.volume_id}\n"
+ f"Volume label: {self.volume_label}\n"
+ f"FS type: {self.fs_type}\n"
+ )
+
+
+class FatDirectoryEntry:
+ # pylint: disable=too-many-instance-attributes
+ def __init__(self, data: bytes, sector: int, offset: int):
+ self.name = data[0:8].decode("ascii").strip()
+ self.ext = data[8:11].decode("ascii").strip()
+ self.attributes = data[11]
+ self.reserved = data[12]
+ self.create_time_tenth = data[13]
+ self.create_time = int.from_bytes(data[14:16], "little")
+ self.create_date = int.from_bytes(data[16:18], "little")
+ self.last_access_date = int.from_bytes(data[18:20], "little")
+ high_cluster = int.from_bytes(data[20:22], "little")
+ self.last_mod_time = int.from_bytes(data[22:24], "little")
+ self.last_mod_date = int.from_bytes(data[24:26], "little")
+ low_cluster = int.from_bytes(data[26:28], "little")
+ self.cluster = (high_cluster << 16) | low_cluster
+ self.size_bytes = int.from_bytes(data[28:32], "little")
+
+ # extra (to help write back to disk)
+ self.sector = sector
+ self.offset = offset
+
+ def as_bytes(self) -> bytes:
+ return (
+ self.name.ljust(8, " ").encode("ascii")
+ + self.ext.ljust(3, " ").encode("ascii")
+ + self.attributes.to_bytes(1, "little")
+ + self.reserved.to_bytes(1, "little")
+ + self.create_time_tenth.to_bytes(1, "little")
+ + self.create_time.to_bytes(2, "little")
+ + self.create_date.to_bytes(2, "little")
+ + self.last_access_date.to_bytes(2, "little")
+ + (self.cluster >> 16).to_bytes(2, "little")
+ + self.last_mod_time.to_bytes(2, "little")
+ + self.last_mod_date.to_bytes(2, "little")
+ + (self.cluster & 0xFFFF).to_bytes(2, "little")
+ + self.size_bytes.to_bytes(4, "little")
+ )
+
+ def whole_name(self):
+ if self.ext:
+ return f"{self.name}.{self.ext}"
+ else:
+ return self.name
+
+ def __str__(self):
+ return (
+ f"Name: {self.name}\n"
+ f"Ext: {self.ext}\n"
+ f"Attributes: {self.attributes}\n"
+ f"Reserved: {self.reserved}\n"
+ f"Create time tenth: {self.create_time_tenth}\n"
+ f"Create time: {self.create_time}\n"
+ f"Create date: {self.create_date}\n"
+ f"Last access date: {self.last_access_date}\n"
+ f"Last mod time: {self.last_mod_time}\n"
+ f"Last mod date: {self.last_mod_date}\n"
+ f"Cluster: {self.cluster}\n"
+ f"Size: {self.size_bytes}\n"
+ )
+
+ def __repr__(self):
+ # convert to dict
+ return str(vars(self))
+
+
+class SectorReader(Protocol):
+ def __call__(self, start_sector: int, num_sectors: int = 1) -> bytes: ...
+
+# pylint: disable=broad-exception-raised
+class Fat16:
+ def __init__(
+ self,
+ start_sector: int,
+ size: int,
+ sector_reader: SectorReader,
+ sector_writer: Callable[[int, bytes], None]
+ ):
+ self.start_sector = start_sector
+ self.size_in_sectors = size
+ self.sector_reader = sector_reader
+ self.sector_writer = sector_writer
+
+ self.boot_sector = FatBootSector(self.sector_reader(start_sector, 1))
+
+ fat_size_in_sectors = (
+ self.boot_sector.sectors_per_fat * self.boot_sector.fat_count
+ )
+ self.fats = self.read_sectors(
+ self.boot_sector.reserved_sectors, fat_size_in_sectors
+ )
+ self.fats_dirty_sectors: Set[int] = set()
+
+ def read_sectors(self, start_sector: int, num_sectors: int) -> bytes:
+ return self.sector_reader(start_sector + self.start_sector,
+ num_sectors)
+
+ def write_sectors(self, start_sector: int, data: bytes) -> None:
+ return self.sector_writer(start_sector + self.start_sector, data)
+
+ def directory_from_bytes(
+ self, data: bytes, start_sector: int
+ ) -> List[FatDirectoryEntry]:
+ """
+ Convert `bytes` into a list of `FatDirectoryEntry` objects.
+ Will ignore long file names.
+ Will stop when it encounters a 0x00 byte.
+ """
+
+ entries = []
+ for i in range(0, len(data), DIRENTRY_SIZE):
+ entry = data[i : i + DIRENTRY_SIZE]
+
+ current_sector = start_sector + (i // SECTOR_SIZE)
+ current_offset = i % SECTOR_SIZE
+
+ if entry[0] == 0:
+ break
+
+ if entry[0] == 0xE5:
+ # Deleted file
+ continue
+
+ if entry[11] & 0xF == 0xF:
+ # Long file name
+ continue
+
+ entries.append(
+ FatDirectoryEntry(entry, current_sector, current_offset)
+ )
+ return entries
+
+ def read_root_directory(self) -> List[FatDirectoryEntry]:
+ root_dir = self.read_sectors(
+ self.boot_sector.root_dir_start(), self.boot_sector.root_dir_size()
+ )
+ return self.directory_from_bytes(
+ root_dir, self.boot_sector.root_dir_start()
+ )
+
+ def read_fat_entry(self, cluster: int) -> int:
+ """
+ Read the FAT entry for the given cluster.
+ """
+ fat_offset = cluster * 2 # FAT16
+ return int.from_bytes(self.fats[fat_offset : fat_offset + 2], "little")
+
+ def write_fat_entry(self, cluster: int, value: int) -> None:
+ """
+ Write the FAT entry for the given cluster.
+ """
+ fat_offset = cluster * 2
+ self.fats = (
+ self.fats[:fat_offset]
+ + value.to_bytes(2, "little")
+ + self.fats[fat_offset + 2 :]
+ )
+ self.fats_dirty_sectors.add(fat_offset // SECTOR_SIZE)
+
+ def flush_fats(self) -> None:
+ """
+ Write the FATs back to the disk.
+ """
+ for sector in self.fats_dirty_sectors:
+ data = self.fats[sector * SECTOR_SIZE : (sector + 1) * SECTOR_SIZE]
+ sector = self.boot_sector.reserved_sectors + sector
+ self.write_sectors(sector, data)
+ self.fats_dirty_sectors = set()
+
+ def next_cluster(self, cluster: int) -> Optional[int]:
+ """
+ Get the next cluster in the chain.
+ If its `None`, then its the last cluster.
+ The function will crash if the next cluster
+ is `FREE` (unexpected) or invalid entry.
+ """
+ fat_entry = self.read_fat_entry(cluster)
+ if fat_entry == 0:
+ raise Exception("Unexpected: FREE cluster")
+ if fat_entry == 1:
+ raise Exception("Unexpected: RESERVED cluster")
+ if fat_entry >= 0xFFF8:
+ return None
+ if fat_entry >= 0xFFF7:
+ raise Exception("Invalid FAT entry")
+
+ return fat_entry
+
+ def next_free_cluster(self) -> int:
+ """
+ Find the next free cluster.
+ """
+ # simple linear search
+ for i in range(2, 0xFFFF):
+ if self.read_fat_entry(i) == 0:
+ return i
+ raise Exception("No free clusters")
+
+ def next_free_cluster_non_continuous(self) -> int:
+ """
+ Find the next free cluster, but makes sure
+ that the cluster before and after it are not allocated.
+ """
+ # simple linear search
+ before = False
+ for i in range(2, 0xFFFF):
+ if self.read_fat_entry(i) == 0:
+ if before and self.read_fat_entry(i + 1) == 0:
+ return i
+ else:
+ before = True
+ else:
+ before = False
+
+ raise Exception("No free clusters")
+
+ def read_cluster(self, cluster: int) -> bytes:
+ """
+ Read the cluster at the given cluster.
+ """
+ return self.read_sectors(
+ self.boot_sector.first_sector_of_cluster(cluster),
+ self.boot_sector.sectors_per_cluster,
+ )
+
+ def write_cluster(self, cluster: int, data: bytes) -> None:
+ """
+ Write the cluster at the given cluster.
+ """
+ assert len(data) == self.boot_sector.cluster_bytes()
+ self.write_sectors(
+ self.boot_sector.first_sector_of_cluster(cluster),
+ data,
+ )
+
+ def read_directory(
+ self, cluster: Optional[int]
+ ) -> List[FatDirectoryEntry]:
+ """
+ Read the directory at the given cluster.
+ """
+ entries = []
+ while cluster is not None:
+ data = self.read_cluster(cluster)
+ entries.extend(
+ self.directory_from_bytes(
+ data, self.boot_sector.first_sector_of_cluster(cluster)
+ )
+ )
+ cluster = self.next_cluster(cluster)
+ return entries
+
+ def add_direntry(
+ self, cluster: Optional[int], name: str, ext: str, attributes: int
+ ) -> FatDirectoryEntry:
+ """
+ Add a new directory entry to the given cluster.
+ If the cluster is `None`, then it will be added to the root directory.
+ """
+
+ def find_free_entry(data: bytes) -> Optional[int]:
+ for i in range(0, len(data), DIRENTRY_SIZE):
+ entry = data[i : i + DIRENTRY_SIZE]
+ if entry[0] == 0 or entry[0] == 0xE5:
+ return i
+ return None
+
+ assert len(name) <= 8, "Name must be 8 characters or less"
+ assert len(ext) <= 3, "Ext must be 3 characters or less"
+ assert attributes % 0x15 != 0x15, "Invalid attributes"
+
+ # initial dummy data
+ new_entry = FatDirectoryEntry(b"\0" * 32, 0, 0)
+ new_entry.name = name.ljust(8, " ")
+ new_entry.ext = ext.ljust(3, " ")
+ new_entry.attributes = attributes
+ new_entry.reserved = 0
+ new_entry.create_time_tenth = 0
+ new_entry.create_time = 0
+ new_entry.create_date = 0
+ new_entry.last_access_date = 0
+ new_entry.last_mod_time = 0
+ new_entry.last_mod_date = 0
+ new_entry.cluster = self.next_free_cluster()
+ new_entry.size_bytes = 0
+
+ # mark as EOF
+ self.write_fat_entry(new_entry.cluster, 0xFFFF)
+
+ if cluster is None:
+ for i in range(self.boot_sector.root_dir_size()):
+ sector_data = self.read_sectors(
+ self.boot_sector.root_dir_start() + i, 1
+ )
+ offset = find_free_entry(sector_data)
+ if offset is not None:
+ new_entry.sector = self.boot_sector.root_dir_start() + i
+ new_entry.offset = offset
+ self.update_direntry(new_entry)
+ return new_entry
+ else:
+ while cluster is not None:
+ data = self.read_cluster(cluster)
+ offset = find_free_entry(data)
+ if offset is not None:
+ new_entry.sector = (
+ self.boot_sector.first_sector_of_cluster(cluster)
+ + (offset // SECTOR_SIZE))
+ new_entry.offset = offset % SECTOR_SIZE
+ self.update_direntry(new_entry)
+ return new_entry
+ cluster = self.next_cluster(cluster)
+
+ raise Exception("No free directory entries")
+
+ def update_direntry(self, entry: FatDirectoryEntry) -> None:
+ """
+ Write the directory entry back to the disk.
+ """
+ sector = self.read_sectors(entry.sector, 1)
+ sector = (
+ sector[: entry.offset]
+ + entry.as_bytes()
+ + sector[entry.offset + DIRENTRY_SIZE :]
+ )
+ self.write_sectors(entry.sector, sector)
+
+ def find_direntry(self, path: str) -> Optional[FatDirectoryEntry]:
+ """
+ Find the directory entry for the given path.
+ """
+ assert path[0] == "/", "Path must start with /"
+
+ path = path[1:] # remove the leading /
+ parts = path.split("/")
+ directory = self.read_root_directory()
+
+ current_entry = None
+
+ for i, part in enumerate(parts):
+ is_last = i == len(parts) - 1
+
+ for entry in directory:
+ if entry.whole_name() == part:
+ current_entry = entry
+ break
+ if current_entry is None:
+ return None
+
+ if is_last:
+ return current_entry
+
+ if current_entry.attributes & 0x10 == 0:
+ raise Exception(
+ f"{current_entry.whole_name()} is not a directory"
+ )
+
+ directory = self.read_directory(current_entry.cluster)
+
+ assert False, "Exited loop with is_last == False"
+
+ def read_file(self, entry: Optional[FatDirectoryEntry]) -> Optional[bytes]:
+ """
+ Read the content of the file at the given path.
+ """
+ if entry is None:
+ return None
+ if entry.attributes & 0x10 != 0:
+ raise Exception(f"{entry.whole_name()} is a directory")
+
+ data = b""
+ cluster: Optional[int] = entry.cluster
+ while cluster is not None and len(data) <= entry.size_bytes:
+ data += self.read_cluster(cluster)
+ cluster = self.next_cluster(cluster)
+ return data[: entry.size_bytes]
+
+ def truncate_file(
+ self,
+ entry: FatDirectoryEntry,
+ new_size: int,
+ allocate_non_continuous: bool = False,
+ ) -> None:
+ """
+ Truncate the file at the given path to the new size.
+ """
+ if entry is None:
+ raise Exception("entry is None")
+ if entry.attributes & 0x10 != 0:
+ raise Exception(f"{entry.whole_name()} is a directory")
+
+ def clusters_from_size(size: int) -> int:
+ return (
+ size + self.boot_sector.cluster_bytes() - 1
+ ) // self.boot_sector.cluster_bytes()
+
+ # First, allocate new FATs if we need to
+ required_clusters = clusters_from_size(new_size)
+ current_clusters = clusters_from_size(entry.size_bytes)
+
+ affected_clusters = set()
+
+ # Keep at least one cluster, easier to manage this way
+ if required_clusters == 0:
+ required_clusters = 1
+ if current_clusters == 0:
+ current_clusters = 1
+
+ cluster: Optional[int]
+
+ if required_clusters > current_clusters:
+ # Allocate new clusters
+ cluster = entry.cluster
+ to_add = required_clusters
+ for _ in range(current_clusters - 1):
+ to_add -= 1
+ assert cluster is not None, "Cluster is None"
+ affected_clusters.add(cluster)
+ cluster = self.next_cluster(cluster)
+ assert required_clusters > 0, "No new clusters to allocate"
+ assert cluster is not None, "Cluster is None"
+ assert (
+ self.next_cluster(cluster) is None
+ ), "Cluster is not the last cluster"
+
+ # Allocate new clusters
+ for _ in range(to_add - 1):
+ if allocate_non_continuous:
+ new_cluster = self.next_free_cluster_non_continuous()
+ else:
+ new_cluster = self.next_free_cluster()
+ self.write_fat_entry(cluster, new_cluster)
+ self.write_fat_entry(new_cluster, 0xFFFF)
+ cluster = new_cluster
+
+ elif required_clusters < current_clusters:
+ # Truncate the file
+ cluster = entry.cluster
+ for _ in range(required_clusters - 1):
+ assert cluster is not None, "Cluster is None"
+ cluster = self.next_cluster(cluster)
+ assert cluster is not None, "Cluster is None"
+
+ next_cluster = self.next_cluster(cluster)
+ # mark last as EOF
+ self.write_fat_entry(cluster, 0xFFFF)
+ # free the rest
+ while next_cluster is not None:
+ cluster = next_cluster
+ next_cluster = self.next_cluster(next_cluster)
+ self.write_fat_entry(cluster, 0)
+
+ self.flush_fats()
+
+ # verify number of clusters
+ cluster = entry.cluster
+ count = 0
+ while cluster is not None:
+ count += 1
+ affected_clusters.add(cluster)
+ cluster = self.next_cluster(cluster)
+ assert (
+ count == required_clusters
+ ), f"Expected {required_clusters} clusters, got {count}"
+
+ # update the size
+ entry.size_bytes = new_size
+ self.update_direntry(entry)
+
+ # trigger every affected cluster
+ for cluster in affected_clusters:
+ first_sector = self.boot_sector.first_sector_of_cluster(cluster)
+ first_sector_data = self.read_sectors(first_sector, 1)
+ self.write_sectors(first_sector, first_sector_data)
+
+ def write_file(self, entry: FatDirectoryEntry, data: bytes) -> None:
+ """
+ Write the content of the file at the given path.
+ """
+ if entry is None:
+ raise Exception("entry is None")
+ if entry.attributes & 0x10 != 0:
+ raise Exception(f"{entry.whole_name()} is a directory")
+
+ data_len = len(data)
+
+ self.truncate_file(entry, data_len)
+
+ cluster: Optional[int] = entry.cluster
+ while cluster is not None:
+ data_to_write = data[: self.boot_sector.cluster_bytes()]
+ if len(data_to_write) < self.boot_sector.cluster_bytes():
+ old_data = self.read_cluster(cluster)
+ data_to_write += old_data[len(data_to_write) :]
+
+ self.write_cluster(cluster, data_to_write)
+ data = data[self.boot_sector.cluster_bytes() :]
+ if len(data) == 0:
+ break
+ cluster = self.next_cluster(cluster)
+
+ assert (
+ len(data) == 0
+ ), "Data was not written completely, clusters missing"
+
+ def create_file(self, path: str) -> Optional[FatDirectoryEntry]:
+ """
+ Create a new file at the given path.
+ """
+ assert path[0] == "/", "Path must start with /"
+
+ path = path[1:] # remove the leading /
+
+ parts = path.split("/")
+
+ directory_cluster = None
+ directory = self.read_root_directory()
+
+ parts, filename = parts[:-1], parts[-1]
+
+ for _, part in enumerate(parts):
+ current_entry = None
+ for entry in directory:
+ if entry.whole_name() == part:
+ current_entry = entry
+ break
+ if current_entry is None:
+ return None
+
+ if current_entry.attributes & 0x10 == 0:
+ raise Exception(
+ f"{current_entry.whole_name()} is not a directory"
+ )
+
+ directory = self.read_directory(current_entry.cluster)
+ directory_cluster = current_entry.cluster
+
+ # add new entry to the directory
+
+ filename, ext = filename.split(".")
+
+ if len(ext) > 3:
+ raise Exception("Ext must be 3 characters or less")
+ if len(filename) > 8:
+ raise Exception("Name must be 8 characters or less")
+
+ for c in filename + ext:
+
+ if c not in ALLOWED_FILE_CHARS:
+ raise Exception("Invalid character in filename")
+
+ return self.add_direntry(directory_cluster, filename, ext, 0)
diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py
index a864c74b12..23307fa2f4 100644
--- a/tests/qemu-iotests/testenv.py
+++ b/tests/qemu-iotests/testenv.py
@@ -250,7 +250,7 @@ def __init__(self, imgfmt: str, imgproto: str, aiomode: str,
self.qemu_img_options = os.getenv('QEMU_IMG_OPTIONS')
self.qemu_nbd_options = os.getenv('QEMU_NBD_OPTIONS')
- is_generic = self.imgfmt not in ['bochs', 'cloop', 'dmg']
+ is_generic = self.imgfmt not in ['bochs', 'cloop', 'dmg', 'vvfat']
self.imgfmt_generic = 'true' if is_generic else 'false'
self.qemu_io_options = f'--cache {self.cachemode} --aio {self.aiomode}'
diff --git a/tests/qemu-iotests/tests/vvfat b/tests/qemu-iotests/tests/vvfat
new file mode 100755
index 0000000000..acdc6ce8ff
--- /dev/null
+++ b/tests/qemu-iotests/tests/vvfat
@@ -0,0 +1,485 @@
+#!/usr/bin/env python3
+# group: rw vvfat
+#
+# Test vvfat driver implementation
+# Here, we use a simple FAT16 implementation and check the behavior of
+# the vvfat driver.
+#
+# Copyright (C) 2024 Amjad Alsharafi <amjadsharafi10@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import shutil
+import iotests
+from iotests import imgfmt, QMPTestCase
+from fat16 import MBR, Fat16, DIRENTRY_SIZE
+
+filesystem = os.path.join(iotests.test_dir, "filesystem")
+
+nbd_sock = iotests.file_path("nbd.sock", base_dir=iotests.sock_dir)
+nbd_uri = "nbd+unix:///disk?socket=" + nbd_sock
+
+SECTOR_SIZE = 512
+
+
+class TestVVFatDriver(QMPTestCase):
+ # pylint: disable=broad-exception-raised
+ def setUp(self) -> None:
+ if os.path.exists(filesystem):
+ if os.path.isdir(filesystem):
+ shutil.rmtree(filesystem)
+ else:
+ raise Exception(f"{filesystem} exists and is not a directory")
+
+ os.mkdir(filesystem)
+
+ # Add some text files to the filesystem
+ for i in range(10):
+ with open(os.path.join(filesystem, f"file{i}.txt"),
+ "w", encoding="ascii") as f:
+ f.write(f"Hello, world! {i}\n")
+
+ # Add 2 large files, above the cluster size (8KB)
+ with open(os.path.join(filesystem, "large1.txt"), "wb") as f:
+ # write 'A' * 1KB, 'B' * 1KB, 'C' * 1KB, ...
+ for i in range(8 * 2): # two clusters
+ f.write(bytes([0x41 + i] * 1024))
+
+ with open(os.path.join(filesystem, "large2.txt"), "wb") as f:
+ # write 'A' * 1KB, 'B' * 1KB, 'C' * 1KB, ...
+ for i in range(8 * 3): # 3 clusters
+ f.write(bytes([0x41 + i] * 1024))
+
+ self.vm = iotests.VM()
+
+ self.vm.add_blockdev(
+ self.vm.qmp_to_opts(
+ {
+ "driver": imgfmt,
+ "node-name": "disk",
+ "rw": "true",
+ "fat-type": "16",
+ "dir": filesystem,
+ }
+ )
+ )
+
+ self.vm.launch()
+
+ self.vm.qmp_log("block-dirty-bitmap-add", **{
+ "node": "disk",
+ "name": "bitmap0",
+ })
+
+ # attach nbd server
+ self.vm.qmp_log(
+ "nbd-server-start",
+ **{"addr": {"type": "unix", "data": {"path": nbd_sock}}},
+ filters=[],
+ )
+
+ self.vm.qmp_log(
+ "nbd-server-add",
+ **{"device": "disk", "writable": True, "bitmap": "bitmap0"},
+ )
+
+ self.qio = iotests.QemuIoInteractive("-f", "raw", nbd_uri)
+
+ def tearDown(self) -> None:
+ self.qio.close()
+ self.vm.shutdown()
+ # print(self.vm.get_log())
+ shutil.rmtree(filesystem)
+
+ def read_sectors(self, sector: int, num: int = 1) -> bytes:
+ """
+ Read `num` sectors starting from `sector` from the `disk`.
+ This uses `QemuIoInteractive` to read the sectors into `stdout` and
+ then parse the output.
+ """
+ self.assertGreater(num, 0)
+
+ # The output contains the content of the sector in hex dump format
+ # We need to extract the content from it
+ output = self.qio.cmd(
+ f"read -v {sector * SECTOR_SIZE} {num * SECTOR_SIZE}")
+
+ # Each row is 16 bytes long, and we are writing `num` sectors
+ rows = num * SECTOR_SIZE // 16
+ output_rows = output.split("\n")[:rows]
+
+ hex_content = "".join(
+ [(row.split(": ")[1]).split(" ")[0] for row in output_rows]
+ )
+ bytes_content = bytes.fromhex(hex_content)
+
+ self.assertEqual(len(bytes_content), num * SECTOR_SIZE)
+
+ return bytes_content
+
+ def write_sectors(self, sector: int, data: bytes) -> None:
+ """
+ Write `data` to the `disk` starting from `sector`.
+ This uses `QemuIoInteractive` to write the data into the disk.
+ """
+
+ self.assertGreater(len(data), 0)
+ self.assertEqual(len(data) % SECTOR_SIZE, 0)
+
+ temp_file = os.path.join(iotests.test_dir, "temp.bin")
+ with open(temp_file, "wb") as f:
+ f.write(data)
+
+ self.qio.cmd(
+ f"write -s {temp_file} {sector * SECTOR_SIZE} {len(data)}"
+ )
+
+ os.remove(temp_file)
+
+ def init_fat16(self):
+ mbr = MBR(self.read_sectors(0))
+ return Fat16(
+ mbr.partition_table[0]["start_lba"],
+ mbr.partition_table[0]["size"],
+ self.read_sectors,
+ self.write_sectors,
+ )
+
+ # Tests
+
+ def test_fat_filesystem(self):
+ """
+ Test that vvfat produce a valid FAT16 and MBR sectors
+ """
+ mbr = MBR(self.read_sectors(0))
+
+ self.assertEqual(mbr.partition_table[0]["status"], 0x80)
+ self.assertEqual(mbr.partition_table[0]["type"], 6)
+
+ fat16 = Fat16(
+ mbr.partition_table[0]["start_lba"],
+ mbr.partition_table[0]["size"],
+ self.read_sectors,
+ self.write_sectors,
+ )
+ self.assertEqual(fat16.boot_sector.bytes_per_sector, 512)
+ self.assertEqual(fat16.boot_sector.volume_label, "QEMU VVFAT")
+
+ def test_read_root_directory(self):
+ """
+ Test the content of the root directory
+ """
+ fat16 = self.init_fat16()
+
+ root_dir = fat16.read_root_directory()
+
+ self.assertEqual(len(root_dir), 13) # 12 + 1 special file
+
+ files = {
+ "QEMU VVF.AT": 0, # special empty file
+ "FILE0.TXT": 16,
+ "FILE1.TXT": 16,
+ "FILE2.TXT": 16,
+ "FILE3.TXT": 16,
+ "FILE4.TXT": 16,
+ "FILE5.TXT": 16,
+ "FILE6.TXT": 16,
+ "FILE7.TXT": 16,
+ "FILE8.TXT": 16,
+ "FILE9.TXT": 16,
+ "LARGE1.TXT": 0x2000 * 2,
+ "LARGE2.TXT": 0x2000 * 3,
+ }
+
+ for entry in root_dir:
+ self.assertIn(entry.whole_name(), files)
+ self.assertEqual(entry.size_bytes, files[entry.whole_name()])
+
+ def test_direntry_as_bytes(self):
+ """
+ Test if we can convert Direntry back to bytes, so that we can write it
+ back to the disk safely.
+ """
+ fat16 = self.init_fat16()
+
+ root_dir = fat16.read_root_directory()
+ first_entry_bytes = fat16.read_sectors(
+ fat16.boot_sector.root_dir_start(), 1)
+
+ # The first entry won't be deleted, so we can compare it with the first
+ # entry in the root directory
+ self.assertEqual(root_dir[0].as_bytes(),
+ first_entry_bytes[:DIRENTRY_SIZE])
+
+ def test_read_files(self):
+ """
+ Test reading the content of the files
+ """
+ fat16 = self.init_fat16()
+
+ for i in range(10):
+ file = fat16.find_direntry(f"/FILE{i}.TXT")
+ self.assertIsNotNone(file)
+ self.assertEqual(
+ fat16.read_file(file), f"Hello, world! {i}\n".encode("ascii")
+ )
+
+ # test large files
+ large1 = fat16.find_direntry("/LARGE1.TXT")
+ with open(os.path.join(filesystem, "large1.txt"), "rb") as f:
+ self.assertEqual(fat16.read_file(large1), f.read())
+
+ large2 = fat16.find_direntry("/LARGE2.TXT")
+ self.assertIsNotNone(large2)
+ with open(os.path.join(filesystem, "large2.txt"), "rb") as f:
+ self.assertEqual(fat16.read_file(large2), f.read())
+
+ def test_write_file_same_content_direct(self):
+ """
+ Similar to `test_write_file_in_same_content`, but we write the file
+ directly clusters and thus we don't go through the modification of
+ direntry.
+ """
+ fat16 = self.init_fat16()
+
+ file = fat16.find_direntry("/FILE0.TXT")
+ self.assertIsNotNone(file)
+
+ data = fat16.read_cluster(file.cluster)
+ fat16.write_cluster(file.cluster, data)
+
+ with open(os.path.join(filesystem, "file0.txt"), "rb") as f:
+ self.assertEqual(fat16.read_file(file), f.read())
+
+ def test_write_file_in_same_content(self):
+ """
+ Test writing the same content to the file back to it
+ """
+ fat16 = self.init_fat16()
+
+ file = fat16.find_direntry("/FILE0.TXT")
+ self.assertIsNotNone(file)
+
+ self.assertEqual(fat16.read_file(file), b"Hello, world! 0\n")
+
+ fat16.write_file(file, b"Hello, world! 0\n")
+ self.assertEqual(fat16.read_file(file), b"Hello, world! 0\n")
+
+ with open(os.path.join(filesystem, "file0.txt"), "rb") as f:
+ self.assertEqual(f.read(), b"Hello, world! 0\n")
+
+ def test_modify_content_same_clusters(self):
+ """
+ Test modifying the content of the file without changing the number of
+ clusters
+ """
+ fat16 = self.init_fat16()
+
+ file = fat16.find_direntry("/FILE0.TXT")
+ self.assertIsNotNone(file)
+
+ new_content = b"Hello, world! Modified\n"
+ self.assertEqual(fat16.read_file(file), b"Hello, world! 0\n")
+
+ fat16.write_file(file, new_content)
+ self.assertEqual(fat16.read_file(file), new_content)
+
+ with open(os.path.join(filesystem, "file0.txt"), "rb") as f:
+ self.assertEqual(f.read(), new_content)
+
+ def test_truncate_file_same_clusters_less(self):
+ """
+ Test truncating the file without changing number of clusters
+ Test decreasing the file size
+ """
+ fat16 = self.init_fat16()
+
+ file = fat16.find_direntry("/FILE0.TXT")
+ self.assertIsNotNone(file)
+
+ self.assertEqual(fat16.read_file(file), b"Hello, world! 0\n")
+
+ fat16.truncate_file(file, 5)
+ new_content = fat16.read_file(file)
+ self.assertEqual(new_content, b"Hello")
+
+ with open(os.path.join(filesystem, "file0.txt"), "rb") as f:
+ self.assertEqual(f.read(), new_content)
+
+ def test_truncate_file_same_clusters_more(self):
+ """
+ Test truncating the file without changing number of clusters
+ Test increase the file size
+ """
+ fat16 = self.init_fat16()
+
+ file = fat16.find_direntry("/FILE0.TXT")
+ self.assertIsNotNone(file)
+
+ self.assertEqual(fat16.read_file(file), b"Hello, world! 0\n")
+
+ fat16.truncate_file(file, 20)
+ new_content = fat16.read_file(file)
+ self.assertIsNotNone(new_content)
+
+ # random pattern will be appended to the file, and its not always the
+ # same
+ self.assertEqual(new_content[:16], b"Hello, world! 0\n")
+ self.assertEqual(len(new_content), 20)
+
+ with open(os.path.join(filesystem, "file0.txt"), "rb") as f:
+ self.assertEqual(f.read(), new_content)
+
+ def test_write_large_file(self):
+ """
+ Test writing a large file
+ """
+ fat16 = self.init_fat16()
+
+ file = fat16.find_direntry("/LARGE1.TXT")
+ self.assertIsNotNone(file)
+
+ # The content of LARGE1 is A * 1KB, B * 1KB, C * 1KB, ..., P * 1KB
+ # Lets change it to be Z * 1KB, Y * 1KB, X * 1KB, ..., K * 1KB
+ # without changing the number of clusters or filesize
+ new_content = b"".join([bytes([0x5A - i] * 1024) for i in range(16)])
+ fat16.write_file(file, new_content)
+ self.assertEqual(fat16.read_file(file), new_content)
+
+ with open(os.path.join(filesystem, "large1.txt"), "rb") as f:
+ self.assertEqual(f.read(), new_content)
+
+ def test_truncate_file_change_clusters_less(self):
+ """
+ Test truncating a file by reducing the number of clusters
+ """
+ fat16 = self.init_fat16()
+
+ file = fat16.find_direntry("/LARGE1.TXT")
+ self.assertIsNotNone(file)
+
+ fat16.truncate_file(file, 1)
+ self.assertEqual(fat16.read_file(file), b"A")
+
+ with open(os.path.join(filesystem, "large1.txt"), "rb") as f:
+ self.assertEqual(f.read(), b"A")
+
+ def test_write_file_change_clusters_less(self):
+ """
+ Test truncating a file by reducing the number of clusters
+ """
+ fat16 = self.init_fat16()
+
+ file = fat16.find_direntry("/LARGE2.TXT")
+ self.assertIsNotNone(file)
+
+ new_content = b"X" * 8 * 1024 + b"Y" * 8 * 1024
+ fat16.write_file(file, new_content)
+ self.assertEqual(fat16.read_file(file), new_content)
+
+ with open(os.path.join(filesystem, "large2.txt"), "rb") as f:
+ self.assertEqual(f.read(), new_content)
+
+ def test_write_file_change_clusters_more(self):
+ """
+ Test truncating a file by increasing the number of clusters
+ """
+ fat16 = self.init_fat16()
+
+ file = fat16.find_direntry("/LARGE2.TXT")
+ self.assertIsNotNone(file)
+
+ # from 3 clusters to 4 clusters
+ new_content = (
+ b"W" * 8 * 1024 +
+ b"X" * 8 * 1024 +
+ b"Y" * 8 * 1024 +
+ b"Z" * 8 * 1024
+ )
+ fat16.write_file(file, new_content)
+ self.assertEqual(fat16.read_file(file), new_content)
+
+ with open(os.path.join(filesystem, "large2.txt"), "rb") as f:
+ self.assertEqual(f.read(), new_content)
+
+ def test_write_file_change_clusters_more_non_contiguous_2_mappings(self):
+ """
+ Test truncating a file by increasing the number of clusters Here we
+ allocate the new clusters in a way that makes them non-contiguous so
+ that we will get 2 cluster mappings for the file
+ """
+ fat16 = self.init_fat16()
+
+ file = fat16.find_direntry("/LARGE1.TXT")
+ self.assertIsNotNone(file)
+
+ # from 2 clusters to 3 clusters with non-contiguous allocation
+ fat16.truncate_file(file, 3 * 0x2000, allocate_non_continuous=True)
+ new_content = b"X" * 8 * 1024 + b"Y" * 8 * 1024 + b"Z" * 8 * 1024
+ fat16.write_file(file, new_content)
+ self.assertEqual(fat16.read_file(file), new_content)
+
+ with open(os.path.join(filesystem, "large1.txt"), "rb") as f:
+ self.assertEqual(f.read(), new_content)
+
+ def test_write_file_change_clusters_more_non_contiguous_3_mappings(self):
+ """
+ Test truncating a file by increasing the number of clusters Here we
+ allocate the new clusters in a way that makes them non-contiguous so
+ that we will get 3 cluster mappings for the file
+ """
+ fat16 = self.init_fat16()
+
+ file = fat16.find_direntry("/LARGE1.TXT")
+ self.assertIsNotNone(file)
+
+ # from 2 clusters to 4 clusters with non-contiguous allocation
+ fat16.truncate_file(file, 4 * 0x2000, allocate_non_continuous=True)
+ new_content = (
+ b"W" * 8 * 1024 +
+ b"X" * 8 * 1024 +
+ b"Y" * 8 * 1024 +
+ b"Z" * 8 * 1024
+ )
+ fat16.write_file(file, new_content)
+ self.assertEqual(fat16.read_file(file), new_content)
+
+ with open(os.path.join(filesystem, "large1.txt"), "rb") as f:
+ self.assertEqual(f.read(), new_content)
+
+ def test_create_file(self):
+ """
+ Test creating a new file
+ """
+ fat16 = self.init_fat16()
+
+ new_file = fat16.create_file("/NEWFILE.TXT")
+
+ self.assertIsNotNone(new_file)
+ self.assertEqual(new_file.size_bytes, 0)
+
+ new_content = b"Hello, world! New file\n"
+ fat16.write_file(new_file, new_content)
+ self.assertEqual(fat16.read_file(new_file), new_content)
+
+ with open(os.path.join(filesystem, "newfile.txt"), "rb") as f:
+ self.assertEqual(f.read(), new_content)
+
+ # TODO: support deleting files
+
+
+if __name__ == "__main__":
+ # This is a specific test for vvfat driver
+ iotests.main(supported_fmts=["vvfat"], supported_protocols=["file"])
diff --git a/tests/qemu-iotests/tests/vvfat.out b/tests/qemu-iotests/tests/vvfat.out
new file mode 100755
index 0000000000..b6f257674e
--- /dev/null
+++ b/tests/qemu-iotests/tests/vvfat.out
@@ -0,0 +1,5 @@
+................
+----------------------------------------------------------------------
+Ran 16 tests
+
+OK
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 31/40] nbd/server: Plumb in new args to nbd_client_add()
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (29 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 30/40] iotests: Add `vvfat` tests Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 32/40] nbd/server: CVE-2024-7409: Cap default max-connections to 100 Michael Tokarev
` (8 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Eric Blake, Vladimir Sementsov-Ogievskiy,
Daniel P . Berrangé, Michael Tokarev
From: Eric Blake <eblake@redhat.com>
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 <vsementsov@yandex-team.ru>
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-ID: <20240807174943.771624-11-eblake@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
[eblake: s/LIMIT/MAX_SECS/ as suggested by Dan]
Signed-off-by: Eric Blake <eblake@redhat.com>
(cherry picked from commit fb1c2aaa981e0a2fa6362c9985f1296b74f055ac)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
index 012256bb02..bf02ddc1e0 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 4ede3b2bd0..3392a5c319 100644
--- a/include/block/nbd.h
+++ b/include/block/nbd.h
@@ -27,6 +27,12 @@
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 */
struct NBDOption {
@@ -338,9 +344,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 74edb2815b..431e543b47 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -120,10 +120,12 @@ typedef struct NBDExportMetaContexts {
struct NBDClient {
int refcount;
void (*close_fn)(NBDClient *client, bool negotiated);
+ void *owner;
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) */
@@ -2755,6 +2757,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);
@@ -2767,14 +2770,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;
@@ -2786,12 +2792,20 @@ 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;
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 f71f5125d8..16b220bdad 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -369,7 +369,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.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 32/40] nbd/server: CVE-2024-7409: Cap default max-connections to 100
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (30 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 31/40] nbd/server: Plumb in new args to nbd_client_add() Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 33/40] nbd/server: CVE-2024-7409: Drop non-negotiating clients Michael Tokarev
` (7 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Eric Blake, Daniel P . Berrangé,
Michael Tokarev
From: Eric Blake <eblake@redhat.com>
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é <berrange@redhat.com>
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-ID: <20240807174943.771624-12-eblake@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
[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 <eblake@redhat.com>
(cherry picked from commit c8a76dbd90c2f48df89b75bef74917f90a59b623)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
(Mjt: minor fixups in qapi/block-export.json)
diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c
index cf21b5e40a..d564d8d234 100644
--- a/block/monitor/block-hmp-cmds.c
+++ b/block/monitor/block-hmp-cmds.c
@@ -413,7 +413,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 bf02ddc1e0..b357ae9bf1 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 3392a5c319..88be104e31 100644
--- a/include/block/nbd.h
+++ b/include/block/nbd.h
@@ -33,6 +33,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 */
struct NBDOption {
diff --git a/qapi/block-export.json b/qapi/block-export.json
index 4627bbc4e6..67d2337f91 100644
--- a/qapi/block-export.json
+++ b/qapi/block-export.json
@@ -24,7 +24,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)
+# (since 5.2; default: 100)
#
# Since: 4.2
##
@@ -55,7 +55,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).
+# (since 5.2; default: 100).
#
# Returns: error if the server is already running.
#
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 33/40] nbd/server: CVE-2024-7409: Drop non-negotiating clients
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (31 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 32/40] nbd/server: CVE-2024-7409: Cap default max-connections to 100 Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 34/40] nbd/server: CVE-2024-7409: Close stray clients at server-stop Michael Tokarev
` (6 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Eric Blake, Daniel P . Berrangé,
Michael Tokarev
From: Eric Blake <eblake@redhat.com>
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é <berrange@redhat.com>
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-ID: <20240807174943.771624-13-eblake@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
[eblake: rebase to changes earlier in series, reduce scope of timer]
Signed-off-by: Eric Blake <eblake@redhat.com>
(cherry picked from commit b9b72cb3ce15b693148bd09cef7e50110566d8a0)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
(Mjt: context fixup in nbd/server.c: lack of WITH_QEMU_LOCK_GUARD in 7.2)
diff --git a/nbd/server.c b/nbd/server.c
index 431e543b47..bfa8d47dad 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -2750,22 +2750,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);
nbd_client_receive_next_request(client);
}
diff --git a/nbd/trace-events b/nbd/trace-events
index b7032ca277..675f880fa1 100644
--- a/nbd/trace-events
+++ b/nbd/trace-events
@@ -73,6 +73,7 @@ nbd_co_receive_request_decode_type(uint64_t handle, uint16_t type, const char *n
nbd_co_receive_request_payload_received(uint64_t handle, uint32_t len) "Payload received: handle = %" PRIu64 ", len = %" PRIu32
nbd_co_receive_align_compliance(const char *op, uint64_t from, uint32_t len, uint32_t align) "client sent non-compliant unaligned %s request: from=0x%" PRIx64 ", len=0x%" PRIx32 ", 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.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 34/40] nbd/server: CVE-2024-7409: Close stray clients at server-stop
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (32 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 33/40] nbd/server: CVE-2024-7409: Drop non-negotiating clients Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 35/40] nbd/server: CVE-2024-7409: Avoid use-after-free when closing server Michael Tokarev
` (5 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Eric Blake, Alexander Ivanov,
Daniel P . Berrangé, Michael Tokarev
From: Eric Blake <eblake@redhat.com>
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 <alexander.ivanov@virtuozzo.com>
Fixes: CVE-2024-7409
CC: qemu-stable@nongnu.org
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-ID: <20240807174943.771624-14-eblake@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
(cherry picked from commit 3e7ef738c8462c45043a1d39f702a0990406a3b3)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
index b357ae9bf1..fea92a0046 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.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 35/40] nbd/server: CVE-2024-7409: Avoid use-after-free when closing server
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (33 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 34/40] nbd/server: CVE-2024-7409: Close stray clients at server-stop Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 36/40] block/blkio: use FUA flag on write zeroes only if supported Michael Tokarev
` (4 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Eric Blake, Andrey Drobyshev, Stefan Hajnoczi,
Michael Tokarev
From: Eric Blake <eblake@redhat.com>
Commit 3e7ef738 plugged the use-after-free of the global nbd_server
object, but overlooked a use-after-free of nbd_server->listener.
Although this race is harder to hit, notice that our shutdown path
first drops the reference count of nbd_server->listener, then triggers
actions that can result in a pending client reaching the
nbd_blockdev_client_closed() callback, which in turn calls
qio_net_listener_set_client_func on a potentially stale object.
If we know we don't want any more clients to connect, and have already
told the listener socket to shut down, then we should not be trying to
update the listener socket's associated function.
Reproducer:
> #!/usr/bin/python3
>
> import os
> from threading import Thread
>
> def start_stop():
> while 1:
> os.system('virsh qemu-monitor-command VM \'{"execute": "nbd-server-start",
+"arguments":{"addr":{"type":"unix","data":{"path":"/tmp/nbd-sock"}}}}\'')
> os.system('virsh qemu-monitor-command VM \'{"execute": "nbd-server-stop"}\'')
>
> def nbd_list():
> while 1:
> os.system('/path/to/build/qemu-nbd -L -k /tmp/nbd-sock')
>
> def test():
> sst = Thread(target=start_stop)
> sst.start()
> nlt = Thread(target=nbd_list)
> nlt.start()
>
> sst.join()
> nlt.join()
>
> test()
Fixes: CVE-2024-7409
Fixes: 3e7ef738c8 ("nbd/server: CVE-2024-7409: Close stray clients at server-stop")
CC: qemu-stable@nongnu.org
Reported-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-ID: <20240822143617.800419-2-eblake@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 3874f5f73c441c52f1c699c848d463b0eda01e4c)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
index fea92a0046..e06c26b0af 100644
--- a/blockdev-nbd.c
+++ b/blockdev-nbd.c
@@ -92,10 +92,13 @@ static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc,
static void nbd_update_server_watch(NBDServerData *s)
{
- if (!s->max_connections || s->connections < s->max_connections) {
- qio_net_listener_set_client_func(s->listener, nbd_accept, NULL, NULL);
- } else {
- qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
+ if (s->listener) {
+ if (!s->max_connections || s->connections < s->max_connections) {
+ qio_net_listener_set_client_func(s->listener, nbd_accept, NULL,
+ NULL);
+ } else {
+ qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
+ }
}
}
@@ -113,6 +116,7 @@ static void nbd_server_free(NBDServerData *server)
*/
qio_net_listener_disconnect(server->listener);
object_unref(OBJECT(server->listener));
+ server->listener = NULL;
QLIST_FOREACH_SAFE(conn, &server->conns, next, tmp) {
qio_channel_shutdown(QIO_CHANNEL(conn->cioc), QIO_CHANNEL_SHUTDOWN_BOTH,
NULL);
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 36/40] block/blkio: use FUA flag on write zeroes only if supported
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (34 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 35/40] nbd/server: CVE-2024-7409: Avoid use-after-free when closing server Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 37/40] target/i386: Do not apply REX to MMX operands Michael Tokarev
` (3 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Stefano Garzarella, Eric Blake,
Philippe Mathieu-Daudé, Stefan Hajnoczi, Michael Tokarev
From: Stefano Garzarella <sgarzare@redhat.com>
libblkio supports BLKIO_REQ_FUA with write zeros requests only since
version 1.4.0, so let's inform the block layer that the blkio driver
supports it only in this case. Otherwise we can have runtime errors
as reported in https://issues.redhat.com/browse/RHEL-32878
Fixes: fd66dbd424 ("blkio: add libblkio block driver")
Cc: qemu-stable@nongnu.org
Buglink: https://issues.redhat.com/browse/RHEL-32878
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Message-id: 20240808080545.40744-1-sgarzare@redhat.com
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 547c4e50929ec6c091d9c16a7b280e829b12b463)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
(Mjt: meson.build fixup for the lack of
v8.0.0-1489-g98b126f5e3 "qapi: add '@fdset' feature for BlockdevOptionsVirtioBlkVhostVdpa")
diff --git a/block/blkio.c b/block/blkio.c
index cb66160268..1fd47c434c 100644
--- a/block/blkio.c
+++ b/block/blkio.c
@@ -808,8 +808,10 @@ static int blkio_file_open(BlockDriverState *bs, QDict *options, int flags,
}
bs->supported_write_flags = BDRV_REQ_FUA | BDRV_REQ_REGISTERED_BUF;
- bs->supported_zero_flags = BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP |
- BDRV_REQ_NO_FALLBACK;
+ bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK;
+#ifdef CONFIG_BLKIO_WRITE_ZEROS_FUA
+ bs->supported_zero_flags |= BDRV_REQ_FUA;
+#endif
qemu_mutex_init(&s->blkio_lock);
qemu_co_mutex_init(&s->bounce_lock);
diff --git a/meson.build b/meson.build
index 787f91855e..16dc9627e0 100644
--- a/meson.build
+++ b/meson.build
@@ -1831,6 +1831,10 @@ config_host_data.set('CONFIG_LZO', lzo.found())
config_host_data.set('CONFIG_MPATH', mpathpersist.found())
config_host_data.set('CONFIG_MPATH_NEW_API', mpathpersist_new_api)
config_host_data.set('CONFIG_BLKIO', blkio.found())
+if blkio.found()
+ config_host_data.set('CONFIG_BLKIO_WRITE_ZEROS_FUA',
+ blkio.version().version_compare('>=1.4.0'))
+endif
config_host_data.set('CONFIG_CURL', curl.found())
config_host_data.set('CONFIG_CURSES', curses.found())
config_host_data.set('CONFIG_GBM', gbm.found())
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 37/40] target/i386: Do not apply REX to MMX operands
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (35 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 36/40] block/blkio: use FUA flag on write zeroes only if supported Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 38/40] module: Prevent crash by resetting local_err in module_load_qom_all() Michael Tokarev
` (2 subsequent siblings)
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Richard Henderson, Paolo Bonzini, Michael Tokarev
From: Richard Henderson <richard.henderson@linaro.org>
Cc: qemu-stable@nongnu.org
Fixes: b3e22b2318a ("target/i386: add core of new i386 decoder")
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2495
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Link: https://lore.kernel.org/r/20240812025844.58956-2-richard.henderson@linaro.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 416f2b16c02c618c0f233372ebfe343f9ee667d4)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc
index 1dfc368456..88de92ed16 100644
--- a/target/i386/tcg/decode-new.c.inc
+++ b/target/i386/tcg/decode-new.c.inc
@@ -1176,7 +1176,10 @@ static bool decode_op(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode,
op->unit = X86_OP_SSE;
}
get_reg:
- op->n = ((get_modrm(s, env) >> 3) & 7) | REX_R(s);
+ op->n = ((get_modrm(s, env) >> 3) & 7);
+ if (op->unit != X86_OP_MMX) {
+ op->n |= REX_R(s);
+ }
break;
case X86_TYPE_E: /* ALU modrm operand */
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 38/40] module: Prevent crash by resetting local_err in module_load_qom_all()
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (36 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 37/40] target/i386: Do not apply REX to MMX operands Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 39/40] crypto/tlscredspsk: Free username on finalize Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 40/40] hw/core/ptimer: fix timer zero period condition for freq > 1GHz Michael Tokarev
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Alexander Ivanov, Claudio Fontana, Denis V . Lunev,
Paolo Bonzini, Michael Tokarev
From: Alexander Ivanov <alexander.ivanov@virtuozzo.com>
Set local_err to NULL after it has been freed in error_report_err(). This
avoids triggering assert(*errp == NULL) failure in error_setv() when
local_err is reused in the loop.
Signed-off-by: Alexander Ivanov <alexander.ivanov@virtuozzo.com>
Reviewed-by: Claudio Fontana <cfontana@suse.de>
Reviewed-by: Denis V. Lunev <den@openvz.org>
Link: https://lore.kernel.org/r/20240809121340.992049-2-alexander.ivanov@virtuozzo.com
[Do the same by moving the declaration instead. - Paolo]
Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 940d802b24e63650e0eacad3714e2ce171cba17c)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/util/module.c b/util/module.c
index 32e263163c..3eb0f06df1 100644
--- a/util/module.c
+++ b/util/module.c
@@ -354,13 +354,13 @@ int module_load_qom(const char *type, Error **errp)
void module_load_qom_all(void)
{
const QemuModinfo *modinfo;
- Error *local_err = NULL;
if (module_loaded_qom_all) {
return;
}
for (modinfo = module_info; modinfo->name != NULL; modinfo++) {
+ Error *local_err = NULL;
if (!modinfo->objs) {
continue;
}
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 39/40] crypto/tlscredspsk: Free username on finalize
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (37 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 38/40] module: Prevent crash by resetting local_err in module_load_qom_all() Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
2024-09-06 5:16 ` [Stable-7.2.14 40/40] hw/core/ptimer: fix timer zero period condition for freq > 1GHz Michael Tokarev
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Peter Maydell, Daniel P . Berrangé,
Philippe Mathieu-Daudé, Michael Tokarev
From: Peter Maydell <peter.maydell@linaro.org>
When the creds->username property is set we allocate memory
for it in qcrypto_tls_creds_psk_prop_set_username(), but
we never free this when the QCryptoTLSCredsPSK is destroyed.
Free the memory in finalize.
This fixes a LeakSanitizer complaint in migration-test:
$ (cd build/asan; ASAN_OPTIONS="fast_unwind_on_malloc=0" QTEST_QEMU_BINARY=./qemu-system-x86_64 ./tests/qtest/migration-test --tap -k -p /x86_64/migration/precopy/unix/tls/psk)
=================================================================
==3867512==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 5 byte(s) in 1 object(s) allocated from:
#0 0x5624e5c99dee in malloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/qemu-system-x86_64+0x218edee) (BuildId: a9e623fa1009a9435c0142c037cd7b8c1ad04ce3)
#1 0x7fb199ae9738 in g_malloc debian/build/deb/../../../glib/gmem.c:128:13
#2 0x7fb199afe583 in g_strdup debian/build/deb/../../../glib/gstrfuncs.c:361:17
#3 0x5624e82ea919 in qcrypto_tls_creds_psk_prop_set_username /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../crypto/tlscredspsk.c:255:23
#4 0x5624e812c6b5 in property_set_str /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../qom/object.c:2277:5
#5 0x5624e8125ce5 in object_property_set /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../qom/object.c:1463:5
#6 0x5624e8136e7c in object_set_properties_from_qdict /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../qom/object_interfaces.c:55:14
#7 0x5624e81372d2 in user_creatable_add_type /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../qom/object_interfaces.c:112:5
#8 0x5624e8137964 in user_creatable_add_qapi /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../qom/object_interfaces.c:157:11
#9 0x5624e891ba3c in qmp_object_add /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../qom/qom-qmp-cmds.c:227:5
#10 0x5624e8af9118 in qmp_marshal_object_add /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/qapi/qapi-commands-qom.c:337:5
#11 0x5624e8bd1d49 in do_qmp_dispatch_bh /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../qapi/qmp-dispatch.c:128:5
#12 0x5624e8cb2531 in aio_bh_call /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../util/async.c:171:5
#13 0x5624e8cb340c in aio_bh_poll /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../util/async.c:218:13
#14 0x5624e8c0be98 in aio_dispatch /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../util/aio-posix.c:423:5
#15 0x5624e8cba3ce in aio_ctx_dispatch /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../util/async.c:360:5
#16 0x7fb199ae0d3a in g_main_dispatch debian/build/deb/../../../glib/gmain.c:3419:28
#17 0x7fb199ae0d3a in g_main_context_dispatch debian/build/deb/../../../glib/gmain.c:4137:7
#18 0x5624e8cbe1d9 in glib_pollfds_poll /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../util/main-loop.c:287:9
#19 0x5624e8cbcb13 in os_host_main_loop_wait /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../util/main-loop.c:310:5
#20 0x5624e8cbc6dc in main_loop_wait /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../util/main-loop.c:589:11
#21 0x5624e6f3f917 in qemu_main_loop /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../system/runstate.c:801:9
#22 0x5624e893379c in qemu_default_main /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../system/main.c:37:14
#23 0x5624e89337e7 in main /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../system/main.c:48:12
#24 0x7fb197972d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#25 0x7fb197972e3f in __libc_start_main csu/../csu/libc-start.c:392:3
#26 0x5624e5c16fa4 in _start (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/qemu-system-x86_64+0x210bfa4) (BuildId: a9e623fa1009a9435c0142c037cd7b8c1ad04ce3)
SUMMARY: AddressSanitizer: 5 byte(s) leaked in 1 allocation(s).
Cc: qemu-stable@nongnu.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Message-ID: <20240819145021.38524-1-peter.maydell@linaro.org>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
(cherry picked from commit 87e012f29f2e47dcd8c385ff8bb8188f9e06d4ea)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/crypto/tlscredspsk.c b/crypto/tlscredspsk.c
index 546cad1c5a..0d6b71a37c 100644
--- a/crypto/tlscredspsk.c
+++ b/crypto/tlscredspsk.c
@@ -243,6 +243,7 @@ qcrypto_tls_creds_psk_finalize(Object *obj)
QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj);
qcrypto_tls_creds_psk_unload(creds);
+ g_free(creds->username);
}
static void
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread* [Stable-7.2.14 40/40] hw/core/ptimer: fix timer zero period condition for freq > 1GHz
2024-09-06 5:15 [Stable-7.2.14 00/40] Patch Round-up for stable 7.2.14, freeze on 2024-09-16 Michael Tokarev
` (38 preceding siblings ...)
2024-09-06 5:16 ` [Stable-7.2.14 39/40] crypto/tlscredspsk: Free username on finalize Michael Tokarev
@ 2024-09-06 5:16 ` Michael Tokarev
39 siblings, 0 replies; 41+ messages in thread
From: Michael Tokarev @ 2024-09-06 5:16 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Jianzhou Yue, Peter Maydell, Michael Tokarev
From: Jianzhou Yue <JianZhou.Yue@verisilicon.com>
The real period is zero when both period and period_frac are zero.
Check the method ptimer_set_freq, if freq is larger than 1000 MHz,
the period is zero, but the period_frac is not, in this case, the
ptimer will work but the current code incorrectly recognizes that
the ptimer is disabled.
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2306
Signed-off-by: JianZhou Yue <JianZhou.Yue@verisilicon.com>
Message-id: 3DA024AEA8B57545AF1B3CAA37077D0FB75E82C8@SHASXM03.verisilicon.com
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit 446e5e8b4515e9a7be69ef6a29852975289bb6f0)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c
index eb5ba1aff7..f1f8109385 100644
--- a/hw/core/ptimer.c
+++ b/hw/core/ptimer.c
@@ -83,7 +83,7 @@ static void ptimer_reload(ptimer_state *s, int delta_adjust)
delta = s->delta = s->limit;
}
- if (s->period == 0) {
+ if (s->period == 0 && s->period_frac == 0) {
if (!qtest_enabled()) {
fprintf(stderr, "Timer with period zero, disabling\n");
}
@@ -309,7 +309,7 @@ void ptimer_run(ptimer_state *s, int oneshot)
assert(s->in_transaction);
- if (was_disabled && s->period == 0) {
+ if (was_disabled && s->period == 0 && s->period_frac == 0) {
if (!qtest_enabled()) {
fprintf(stderr, "Timer with period zero, disabling\n");
}
diff --git a/tests/unit/ptimer-test.c b/tests/unit/ptimer-test.c
index 04b5f4e3d0..08240594bb 100644
--- a/tests/unit/ptimer-test.c
+++ b/tests/unit/ptimer-test.c
@@ -763,6 +763,33 @@ static void check_oneshot_with_load_0(gconstpointer arg)
ptimer_free(ptimer);
}
+static void check_freq_more_than_1000M(gconstpointer arg)
+{
+ const uint8_t *policy = arg;
+ ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy);
+ bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
+
+ triggered = false;
+
+ ptimer_transaction_begin(ptimer);
+ ptimer_set_freq(ptimer, 2000000000);
+ ptimer_set_limit(ptimer, 8, 1);
+ ptimer_run(ptimer, 1);
+ ptimer_transaction_commit(ptimer);
+
+ qemu_clock_step(3);
+
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 3 : 2);
+ g_assert_false(triggered);
+
+ qemu_clock_step(1);
+
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
+ g_assert_true(triggered);
+
+ ptimer_free(ptimer);
+}
+
static void add_ptimer_tests(uint8_t policy)
{
char policy_name[256] = "";
@@ -857,6 +884,12 @@ static void add_ptimer_tests(uint8_t policy)
policy_name),
g_memdup2(&policy, 1), check_oneshot_with_load_0, g_free);
g_free(tmp);
+
+ g_test_add_data_func_full(
+ tmp = g_strdup_printf("/ptimer/freq_more_than_1000M policy=%s",
+ policy_name),
+ g_memdup2(&policy, 1), check_freq_more_than_1000M, g_free);
+ g_free(tmp);
}
static void add_all_ptimer_policies_comb_tests(void)
--
2.39.2
^ permalink raw reply related [flat|nested] 41+ messages in thread